1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
{ config, pkgs, lib, ...}:
let
inherit (builtins)
toString ;
inherit (lib)
concatMapStrings filterAttrs mapAttrsToList mkOption
types unique ;
inherit (types)
attrsOf path str submodule ;
explicit = filterAttrs (n: v: n != "_module" && v != null);
apps = explicit config.nixsap.apps.cli;
exec = name: { user, command, ... }:
let
uid = toString config.users.users.${user}.uid;
gid = uid;
src = pkgs.writeText "${name}.c" ''
#include <unistd.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
int main (int __attribute__((unused)) argc, char *argv[])
{
int rc;
if (getuid() != ${uid}) {
if (geteuid() != 0) {
fprintf(stderr, "Forbidden.\n");
return EXIT_FAILURE;
}
rc = initgroups("${user}", ${gid});
if (0 != rc) {
perror("initgroups()");
return EXIT_FAILURE;
}
rc = setgid(${gid});
if (0 != rc) {
perror("setgid()");
return EXIT_FAILURE;
}
rc = setuid(${uid});
if (0 != rc) {
perror("setuid()");
return EXIT_FAILURE;
}
if ((getuid() != ${uid}) || (geteuid() != ${uid})) {
fprintf(stderr, "Something went wrong.\n");
return EXIT_FAILURE;
}
struct passwd * pw = getpwuid(${uid});
if (NULL == pw) {
perror("getpwuid()");
return EXIT_FAILURE;
}
if (NULL != pw->pw_dir) {
rc = chdir(pw->pw_dir);
if (0 != rc) {
rc = chdir("/");
}
} else {
rc = chdir("/");
}
if (0 != rc) {
perror("chdir()");
return EXIT_FAILURE;
}
}
argv[0] = "${command}";
execv(argv[0], argv);
perror("execv()");
return EXIT_FAILURE;
}
'';
in pkgs.runCommand name {} "gcc -Wall -Wextra -Werror -s -std=gnu99 -O2 ${src} -o $out";
cliapp = submodule({name, ...}:
{
options = {
user = mkOption {
description = ''
User (and group) to run as. Only users in this group can execute
this application.
'';
type = str;
default = name;
};
command = mkOption {
description = "Path to executable";
type = path;
};
};
});
in {
options.nixsap = {
apps.cli = mkOption {
description = ''
Command line applications that should run as other users and likely
have special privileges, e. g. to access secret keys. This is
implemented with setuid-wrappers. Each wrapper is launched as root,
immediately switches to specified user, then executes something
useful. This is like sudo, but access is controlled via wrapper's
group: only users in wrapper's group can execute the wrapper.
Starting as set-uid-non-root is not sufficient, because we might
need supplementary groups, like "keys".
'';
type = attrsOf cliapp;
default = {};
};
};
config = {
nixsap.system.users.daemons = unique (mapAttrsToList (_: a: a.user) apps);
security.setuidOwners = mapAttrsToList (n: a:
{ program = n;
owner = "root";
group = a.user;
setuid = true;
setgid = false;
permissions = "u+rx,g+x,o=";
source = exec n a;
}) apps;
};
}
|