diff options
Diffstat (limited to 'regress/check-perm.c')
-rw-r--r-- | regress/check-perm.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/regress/check-perm.c b/regress/check-perm.c new file mode 100644 index 000000000..dac307d24 --- /dev/null +++ b/regress/check-perm.c | |||
@@ -0,0 +1,205 @@ | |||
1 | /* | ||
2 | * Placed in the public domain | ||
3 | */ | ||
4 | |||
5 | /* $OpenBSD: modpipe.c,v 1.6 2013/11/21 03:16:47 djm Exp $ */ | ||
6 | |||
7 | #include "includes.h" | ||
8 | |||
9 | #include <sys/types.h> | ||
10 | #include <sys/stat.h> | ||
11 | #include <unistd.h> | ||
12 | #include <stdio.h> | ||
13 | #include <string.h> | ||
14 | #include <stdarg.h> | ||
15 | #include <stdlib.h> | ||
16 | #include <errno.h> | ||
17 | #include <pwd.h> | ||
18 | #ifdef HAVE_LIBGEN_H | ||
19 | #include <libgen.h> | ||
20 | #endif | ||
21 | |||
22 | static void | ||
23 | fatal(const char *fmt, ...) | ||
24 | { | ||
25 | va_list args; | ||
26 | |||
27 | va_start(args, fmt); | ||
28 | vfprintf(stderr, fmt, args); | ||
29 | fputc('\n', stderr); | ||
30 | va_end(args); | ||
31 | exit(1); | ||
32 | } | ||
33 | /* Based on session.c. NB. keep tests in sync */ | ||
34 | static void | ||
35 | safely_chroot(const char *path, uid_t uid) | ||
36 | { | ||
37 | const char *cp; | ||
38 | char component[PATH_MAX]; | ||
39 | struct stat st; | ||
40 | |||
41 | if (*path != '/') | ||
42 | fatal("chroot path does not begin at root"); | ||
43 | if (strlen(path) >= sizeof(component)) | ||
44 | fatal("chroot path too long"); | ||
45 | |||
46 | /* | ||
47 | * Descend the path, checking that each component is a | ||
48 | * root-owned directory with strict permissions. | ||
49 | */ | ||
50 | for (cp = path; cp != NULL;) { | ||
51 | if ((cp = strchr(cp, '/')) == NULL) | ||
52 | strlcpy(component, path, sizeof(component)); | ||
53 | else { | ||
54 | cp++; | ||
55 | memcpy(component, path, cp - path); | ||
56 | component[cp - path] = '\0'; | ||
57 | } | ||
58 | |||
59 | /* debug3("%s: checking '%s'", __func__, component); */ | ||
60 | |||
61 | if (stat(component, &st) != 0) | ||
62 | fatal("%s: stat(\"%s\"): %s", __func__, | ||
63 | component, strerror(errno)); | ||
64 | if (st.st_uid != 0 || (st.st_mode & 022) != 0) | ||
65 | fatal("bad ownership or modes for chroot " | ||
66 | "directory %s\"%s\"", | ||
67 | cp == NULL ? "" : "component ", component); | ||
68 | if (!S_ISDIR(st.st_mode)) | ||
69 | fatal("chroot path %s\"%s\" is not a directory", | ||
70 | cp == NULL ? "" : "component ", component); | ||
71 | |||
72 | } | ||
73 | |||
74 | if (chdir(path) == -1) | ||
75 | fatal("Unable to chdir to chroot path \"%s\": " | ||
76 | "%s", path, strerror(errno)); | ||
77 | } | ||
78 | |||
79 | /* from platform.c */ | ||
80 | int | ||
81 | platform_sys_dir_uid(uid_t uid) | ||
82 | { | ||
83 | if (uid == 0) | ||
84 | return 1; | ||
85 | #ifdef PLATFORM_SYS_DIR_UID | ||
86 | if (uid == PLATFORM_SYS_DIR_UID) | ||
87 | return 1; | ||
88 | #endif | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | /* from auth.c */ | ||
93 | int | ||
94 | auth_secure_path(const char *name, struct stat *stp, const char *pw_dir, | ||
95 | uid_t uid, char *err, size_t errlen) | ||
96 | { | ||
97 | char buf[PATH_MAX], homedir[PATH_MAX]; | ||
98 | char *cp; | ||
99 | int comparehome = 0; | ||
100 | struct stat st; | ||
101 | |||
102 | if (realpath(name, buf) == NULL) { | ||
103 | snprintf(err, errlen, "realpath %s failed: %s", name, | ||
104 | strerror(errno)); | ||
105 | return -1; | ||
106 | } | ||
107 | if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL) | ||
108 | comparehome = 1; | ||
109 | |||
110 | if (!S_ISREG(stp->st_mode)) { | ||
111 | snprintf(err, errlen, "%s is not a regular file", buf); | ||
112 | return -1; | ||
113 | } | ||
114 | if ((!platform_sys_dir_uid(stp->st_uid) && stp->st_uid != uid) || | ||
115 | (stp->st_mode & 022) != 0) { | ||
116 | snprintf(err, errlen, "bad ownership or modes for file %s", | ||
117 | buf); | ||
118 | return -1; | ||
119 | } | ||
120 | |||
121 | /* for each component of the canonical path, walking upwards */ | ||
122 | for (;;) { | ||
123 | if ((cp = dirname(buf)) == NULL) { | ||
124 | snprintf(err, errlen, "dirname() failed"); | ||
125 | return -1; | ||
126 | } | ||
127 | strlcpy(buf, cp, sizeof(buf)); | ||
128 | |||
129 | if (stat(buf, &st) < 0 || | ||
130 | (!platform_sys_dir_uid(st.st_uid) && st.st_uid != uid) || | ||
131 | (st.st_mode & 022) != 0) { | ||
132 | snprintf(err, errlen, | ||
133 | "bad ownership or modes for directory %s", buf); | ||
134 | return -1; | ||
135 | } | ||
136 | |||
137 | /* If are past the homedir then we can stop */ | ||
138 | if (comparehome && strcmp(homedir, buf) == 0) | ||
139 | break; | ||
140 | |||
141 | /* | ||
142 | * dirname should always complete with a "/" path, | ||
143 | * but we can be paranoid and check for "." too | ||
144 | */ | ||
145 | if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) | ||
146 | break; | ||
147 | } | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | static void | ||
152 | usage(void) | ||
153 | { | ||
154 | fprintf(stderr, "check-perm -m [chroot | keys-command] [path]\n"); | ||
155 | exit(1); | ||
156 | } | ||
157 | |||
158 | int | ||
159 | main(int argc, char **argv) | ||
160 | { | ||
161 | const char *path = "."; | ||
162 | char errmsg[256]; | ||
163 | int ch, mode = -1; | ||
164 | extern char *optarg; | ||
165 | extern int optind; | ||
166 | struct stat st; | ||
167 | |||
168 | while ((ch = getopt(argc, argv, "hm:")) != -1) { | ||
169 | switch (ch) { | ||
170 | case 'm': | ||
171 | if (strcasecmp(optarg, "chroot") == 0) | ||
172 | mode = 1; | ||
173 | else if (strcasecmp(optarg, "keys-command") == 0) | ||
174 | mode = 2; | ||
175 | else { | ||
176 | fprintf(stderr, "Invalid -m option\n"), | ||
177 | usage(); | ||
178 | } | ||
179 | break; | ||
180 | default: | ||
181 | usage(); | ||
182 | } | ||
183 | } | ||
184 | argc -= optind; | ||
185 | argv += optind; | ||
186 | |||
187 | if (argc > 1) | ||
188 | usage(); | ||
189 | else if (argc == 1) | ||
190 | path = argv[0]; | ||
191 | |||
192 | if (mode == 1) | ||
193 | safely_chroot(path, getuid()); | ||
194 | else if (mode == 2) { | ||
195 | if (stat(path, &st) < 0) | ||
196 | fatal("Could not stat %s: %s", path, strerror(errno)); | ||
197 | if (auth_secure_path(path, &st, NULL, 0, | ||
198 | errmsg, sizeof(errmsg)) != 0) | ||
199 | fatal("Unsafe %s: %s", path, errmsg); | ||
200 | } else { | ||
201 | fprintf(stderr, "Invalid mode\n"); | ||
202 | usage(); | ||
203 | } | ||
204 | return 0; | ||
205 | } | ||