linux-qubasis
linux oasis port as a qubes template
git clone https://9o.is/git/linux-qubasis.git
0014-doas-Port-to-linux-musl.patch
(14338B)
1 From cca6c84b472255c4d015d7d97225791796f61392 Mon Sep 17 00:00:00 2001
2 From: Michael Forney <mforney@mforney.org>
3 Date: Sun, 26 Feb 2017 16:50:55 -0800
4 Subject: [PATCH] doas: Port to linux/musl
5
6 Remove -a login style option and BSD authentication. Instead, compare
7 against shadow file.
8
9 Use timestamp files in /run/doas instead of TIOC*VERAUTH to implement
10 persist.
11
12 Use initgroups/setgid/setuid instead of setusercontext.
13
14 Provide UID_MAX and GID_MAX defaults.
15
16 Use LOGIN_NAME_MAX instead of _PW_NAME_LEN.
17
18 Remove call to closefrom.
19
20 Replace calls to errc with err after setting errno.
21
22 Call openlog at start to set syslog identity.
23
24 Remove unveil/pledge since they aren't supported on Linux.
25
26 Simplify handling of PATH in the environment since we don't have
27 login.conf with per-user default PATH.
28 ---
29 usr.bin/doas/doas.1 | 9 ---
30 usr.bin/doas/doas.c | 168 +++++++++++++----------------------------
31 usr.bin/doas/doas.h | 6 +-
32 usr.bin/doas/env.c | 17 ++---
33 usr.bin/doas/parse.y | 1 +
34 usr.bin/doas/persist.c | 133 ++++++++++++++++++++++++++++++++
35 6 files changed, 198 insertions(+), 136 deletions(-)
36 create mode 100644 usr.bin/doas/persist.c
37
38 diff --git a/usr.bin/doas/doas.1 b/usr.bin/doas/doas.1
39 index 25827cc7104..3542680faf5 100644
40 --- a/usr.bin/doas/doas.1
41 +++ b/usr.bin/doas/doas.1
42 @@ -22,7 +22,6 @@
43 .Sh SYNOPSIS
44 .Nm doas
45 .Op Fl Lns
46 -.Op Fl a Ar style
47 .Op Fl C Ar config
48 .Op Fl u Ar user
49 .Ar command
50 @@ -67,14 +66,6 @@ The working directory is not changed.
51 .Pp
52 The options are as follows:
53 .Bl -tag -width tenletters
54 -.It Fl a Ar style
55 -Use the specified authentication style when validating the user,
56 -as allowed by
57 -.Pa /etc/login.conf .
58 -A list of doas-specific authentication methods may be configured by adding an
59 -.Sq auth-doas
60 -entry in
61 -.Xr login.conf 5 .
62 .It Fl C Ar config
63 Parse and check the configuration file
64 .Ar config ,
65 diff --git a/usr.bin/doas/doas.c b/usr.bin/doas/doas.c
66 index 3999b2e2f64..32532359267 100644
67 --- a/usr.bin/doas/doas.c
68 +++ b/usr.bin/doas/doas.c
69 @@ -20,8 +20,6 @@
70 #include <sys/ioctl.h>
71
72 #include <limits.h>
73 -#include <login_cap.h>
74 -#include <bsd_auth.h>
75 #include <readpassphrase.h>
76 #include <string.h>
77 #include <stdio.h>
78 @@ -33,13 +31,22 @@
79 #include <syslog.h>
80 #include <errno.h>
81 #include <fcntl.h>
82 +#include <shadow.h>
83
84 #include "doas.h"
85
86 +#ifndef UID_MAX
87 +#define UID_MAX 65535
88 +#endif
89 +
90 +#ifndef GID_MAX
91 +#define GID_MAX 65535
92 +#endif
93 +
94 static void __dead
95 usage(void)
96 {
97 - fprintf(stderr, "usage: doas [-Lns] [-a style] [-C config] [-u user]"
98 + fprintf(stderr, "usage: doas [-Lns] [-C config] [-u user]"
99 " command [arg ...]\n");
100 exit(1);
101 }
102 @@ -203,16 +210,28 @@ checkconfig(const char *confpath, int argc, char **argv,
103 }
104
105 static int
106 -authuser_checkpass(char *myname, char *login_style)
107 +verifypasswd(const char *user, const char *pass)
108 +{
109 + struct spwd *sp;
110 + char *p1, *p2;
111 +
112 + sp = getspnam(user);
113 + if (!sp)
114 + return 0;
115 + p1 = sp->sp_pwdp;
116 + if (p1[0] == '!' || p1[0] == '*')
117 + return 0;
118 + p2 = crypt(pass, p1);
119 + if (!p2)
120 + return 0;
121 + return strcmp(p1, p2) == 0;
122 +}
123 +
124 +static int
125 +authuser_checkpass(char *myname)
126 {
127 char *challenge = NULL, *response, rbuf[1024], cbuf[128];
128 - auth_session_t *as;
129
130 - if (!(as = auth_userchallenge(myname, login_style, "auth-doas",
131 - &challenge))) {
132 - warnx("Authentication failed");
133 - return AUTH_FAILED;
134 - }
135 if (!challenge) {
136 char host[HOST_NAME_MAX + 1];
137
138 @@ -225,14 +244,12 @@ authuser_checkpass(char *myname, char *login_style)
139 response = readpassphrase(challenge, rbuf, sizeof(rbuf),
140 RPP_REQUIRE_TTY);
141 if (response == NULL && errno == ENOTTY) {
142 - syslog(LOG_AUTHPRIV | LOG_NOTICE,
143 - "tty required for %s", myname);
144 + syslog(LOG_NOTICE, "tty required for %s", myname);
145 errx(1, "a tty is required");
146 }
147 - if (!auth_userresponse(as, response, 0)) {
148 + if (!verifypasswd(myname, response)) {
149 explicit_bzero(rbuf, sizeof(rbuf));
150 - syslog(LOG_AUTHPRIV | LOG_NOTICE,
151 - "failed auth for %s", myname);
152 + syslog(LOG_NOTICE, "failed auth for %s", myname);
153 warnx("Authentication failed");
154 return AUTH_FAILED;
155 }
156 @@ -241,79 +258,36 @@ authuser_checkpass(char *myname, char *login_style)
157 }
158
159 static void
160 -authuser(char *myname, char *login_style, int persist)
161 +authuser(char *myname, int persist)
162 {
163 - int i, fd = -1;
164 + int i, fd = -1, valid = 0;
165
166 - if (persist)
167 - fd = open("/dev/tty", O_RDWR);
168 - if (fd != -1) {
169 - if (ioctl(fd, TIOCCHKVERAUTH) == 0)
170 + if (persist) {
171 + fd = openpersist(&valid);
172 + if (valid)
173 goto good;
174 }
175 for (i = 0; i < AUTH_RETRIES; i++) {
176 - if (authuser_checkpass(myname, login_style) == AUTH_OK)
177 + if (authuser_checkpass(myname) == AUTH_OK)
178 goto good;
179 }
180 exit(1);
181 good:
182 if (fd != -1) {
183 - int secs = 5 * 60;
184 - ioctl(fd, TIOCSETVERAUTH, &secs);
185 + setpersist(fd);
186 close(fd);
187 }
188 }
189
190 -int
191 -unveilcommands(const char *ipath, const char *cmd)
192 -{
193 - char *path = NULL, *p;
194 - int unveils = 0;
195 -
196 - if (strchr(cmd, '/') != NULL) {
197 - if (unveil(cmd, "x") != -1)
198 - unveils++;
199 - goto done;
200 - }
201 -
202 - if (!ipath) {
203 - errno = ENOENT;
204 - goto done;
205 - }
206 - path = strdup(ipath);
207 - if (!path) {
208 - errno = ENOENT;
209 - goto done;
210 - }
211 - for (p = path; p && *p; ) {
212 - char buf[PATH_MAX];
213 - char *cp = strsep(&p, ":");
214 -
215 - if (cp) {
216 - int r = snprintf(buf, sizeof buf, "%s/%s", cp, cmd);
217 - if (r >= 0 && r < sizeof buf) {
218 - if (unveil(buf, "x") != -1)
219 - unveils++;
220 - }
221 - }
222 - }
223 -done:
224 - free(path);
225 - return (unveils);
226 -}
227 -
228 int
229 main(int argc, char **argv)
230 {
231 - const char *safepath = "/bin:/sbin:/usr/bin:/usr/sbin:"
232 - "/usr/local/bin:/usr/local/sbin";
233 const char *confpath = NULL;
234 char *shargv[] = { NULL, NULL };
235 char *sh;
236 - const char *p;
237 const char *cmd;
238 char cmdline[LINE_MAX];
239 - char mypwbuf[_PW_BUF_LEN], targpwbuf[_PW_BUF_LEN];
240 + char mypwbuf[1024], targpwbuf[1024];
241 struct passwd mypwstore, targpwstore;
242 struct passwd *mypw, *targpw;
243 const struct rule *rule;
244 @@ -326,28 +300,20 @@ main(int argc, char **argv)
245 int nflag = 0;
246 char cwdpath[PATH_MAX];
247 const char *cwd;
248 - char *login_style = NULL;
249 char **envp;
250
251 setprogname("doas");
252 -
253 - closefrom(STDERR_FILENO + 1);
254 + openlog("doas", 0, LOG_AUTHPRIV);
255
256 uid = getuid();
257
258 - while ((ch = getopt(argc, argv, "a:C:Lnsu:")) != -1) {
259 + while ((ch = getopt(argc, argv, "C:Lnsu:")) != -1) {
260 switch (ch) {
261 - case 'a':
262 - login_style = optarg;
263 - break;
264 case 'C':
265 confpath = optarg;
266 break;
267 case 'L':
268 - i = open("/dev/tty", O_RDWR);
269 - if (i != -1)
270 - ioctl(i, TIOCCLRVERAUTH);
271 - exit(i == -1);
272 + exit(clearpersist() != 0);
273 case 'u':
274 if (parseuid(optarg, &target) != 0)
275 errx(1, "unknown user");
276 @@ -418,50 +384,30 @@ main(int argc, char **argv)
277 rv = permit(uid, groups, ngroups, &rule, target, cmd,
278 (const char **)argv + 1);
279 if (rv != 0) {
280 - syslog(LOG_AUTHPRIV | LOG_NOTICE,
281 - "command not permitted for %s: %s", mypw->pw_name, cmdline);
282 - errc(1, EPERM, NULL);
283 + syslog(LOG_NOTICE, "command not permitted for %s: %s", mypw->pw_name, cmdline);
284 + errno = EPERM;
285 + err(1, NULL);
286 }
287
288 if (!(rule->options & NOPASS)) {
289 if (nflag)
290 errx(1, "Authentication required");
291
292 - authuser(mypw->pw_name, login_style, rule->options & PERSIST);
293 + authuser(mypw->pw_name, rule->options & PERSIST);
294 }
295
296 - if ((p = getenv("PATH")) != NULL)
297 - formerpath = strdup(p);
298 - if (formerpath == NULL)
299 - formerpath = "";
300 -
301 - if (unveil(_PATH_LOGIN_CONF, "r") == -1)
302 - err(1, "unveil %s", _PATH_LOGIN_CONF);
303 - if (unveil(_PATH_LOGIN_CONF ".db", "r") == -1)
304 - err(1, "unveil %s.db", _PATH_LOGIN_CONF);
305 - if (unveil(_PATH_LOGIN_CONF_D, "r") == -1)
306 - err(1, "unveil %s", _PATH_LOGIN_CONF_D);
307 - if (rule->cmd) {
308 - if (setenv("PATH", safepath, 1) == -1)
309 - err(1, "failed to set PATH '%s'", safepath);
310 - }
311 - if (unveilcommands(getenv("PATH"), cmd) == 0)
312 - goto fail;
313 -
314 - if (pledge("stdio rpath getpw exec id", NULL) == -1)
315 - err(1, "pledge");
316 -
317 rv = getpwuid_r(target, &targpwstore, targpwbuf, sizeof(targpwbuf), &targpw);
318 if (rv != 0)
319 err(1, "getpwuid_r failed");
320 if (targpw == NULL)
321 errx(1, "no passwd entry for target");
322
323 - if (setusercontext(NULL, targpw, target, LOGIN_SETGROUP |
324 - LOGIN_SETPATH |
325 - LOGIN_SETPRIORITY | LOGIN_SETRESOURCES | LOGIN_SETUMASK |
326 - LOGIN_SETUSER | LOGIN_SETENV | LOGIN_SETRTABLE) != 0)
327 - errx(1, "failed to set user context for target");
328 + if (initgroups(targpw->pw_name, targpw->pw_gid) == -1)
329 + err(1, "initgroups");
330 + if (setgid(targpw->pw_gid) == -1)
331 + err(1, "setgid");
332 + if (setuid(targpw->pw_uid) == -1)
333 + err(1, "setuid");
334
335 if (pledge("stdio rpath exec", NULL) == -1)
336 err(1, "pledge");
337 @@ -475,23 +421,17 @@ main(int argc, char **argv)
338 err(1, "pledge");
339
340 if (!(rule->options & NOLOG)) {
341 - syslog(LOG_AUTHPRIV | LOG_INFO,
342 - "%s ran command %s as %s from %s",
343 + syslog(LOG_INFO, "%s ran command %s as %s from %s",
344 mypw->pw_name, cmdline, targpw->pw_name, cwd);
345 }
346
347 envp = prepenv(rule, mypw, targpw);
348
349 - /* setusercontext set path for the next process, so reset it for us */
350 if (rule->cmd) {
351 if (setenv("PATH", safepath, 1) == -1)
352 err(1, "failed to set PATH '%s'", safepath);
353 - } else {
354 - if (setenv("PATH", formerpath, 1) == -1)
355 - err(1, "failed to set PATH '%s'", formerpath);
356 }
357 execvpe(cmd, argv, envp);
358 -fail:
359 if (errno == ENOENT)
360 errx(1, "%s: command not found", cmd);
361 err(1, "%s", cmd);
362 diff --git a/usr.bin/doas/doas.h b/usr.bin/doas/doas.h
363 index ce6a03618ac..363e2626c23 100644
364 --- a/usr.bin/doas/doas.h
365 +++ b/usr.bin/doas/doas.h
366 @@ -29,13 +29,17 @@ extern struct rule **rules;
367 extern size_t nrules;
368 extern int parse_error;
369
370 -extern const char *formerpath;
371 +extern const char *safepath;
372
373 struct passwd;
374
375 char **prepenv(const struct rule *, const struct passwd *,
376 const struct passwd *);
377
378 +int openpersist(int *valid);
379 +int setpersist(int fd);
380 +int clearpersist(void);
381 +
382 #define PERMIT -1
383 #define DENY 2
384
385 diff --git a/usr.bin/doas/env.c b/usr.bin/doas/env.c
386 index 2d93a4089b6..dc9be691955 100644
387 --- a/usr.bin/doas/env.c
388 +++ b/usr.bin/doas/env.c
389 @@ -28,7 +28,7 @@
390
391 #include "doas.h"
392
393 -const char *formerpath;
394 +const char *safepath = "/bin";
395
396 struct envnode {
397 RB_ENTRY(envnode) node;
398 @@ -103,7 +103,7 @@ createenv(const struct rule *rule, const struct passwd *mypw,
399 addnode(env, "DOAS_USER", mypw->pw_name);
400 addnode(env, "HOME", targpw->pw_dir);
401 addnode(env, "LOGNAME", targpw->pw_name);
402 - addnode(env, "PATH", getenv("PATH"));
403 + addnode(env, "PATH", safepath);
404 addnode(env, "SHELL", targpw->pw_shell);
405 addnode(env, "USER", targpw->pw_name);
406
407 @@ -200,17 +200,10 @@ fillenv(struct env *env, const char **envlist)
408 /* assign value or inherit from environ */
409 if (eq) {
410 val = eq + 1;
411 - if (*val == '$') {
412 - if (strcmp(val + 1, "PATH") == 0)
413 - val = formerpath;
414 - else
415 - val = getenv(val + 1);
416 - }
417 + if (*val == '$')
418 + val = getenv(val + 1);
419 } else {
420 - if (strcmp(name, "PATH") == 0)
421 - val = formerpath;
422 - else
423 - val = getenv(name);
424 + val = getenv(name);
425 }
426 /* at last, we have something to insert */
427 if (val) {
428 diff --git a/usr.bin/doas/parse.y b/usr.bin/doas/parse.y
429 index 604becb5445..e5fc912a9c4 100644
430 --- a/usr.bin/doas/parse.y
431 +++ b/usr.bin/doas/parse.y
432 @@ -20,6 +20,7 @@
433 #include <ctype.h>
434 #include <limits.h>
435 #include <unistd.h>
436 +#include <stdlib.h>
437 #include <stdint.h>
438 #include <stdarg.h>
439 #include <stdio.h>
440 diff --git a/usr.bin/doas/persist.c b/usr.bin/doas/persist.c
441 new file mode 100644
442 index 00000000000..4ad1bf1efbf
443 --- /dev/null
444 +++ b/usr.bin/doas/persist.c
445 @@ -0,0 +1,133 @@
446 +#include <errno.h>
447 +#include <fcntl.h>
448 +#include <limits.h>
449 +#include <stdio.h>
450 +#include <stdlib.h>
451 +#include <string.h>
452 +#include <sys/stat.h>
453 +#include <sys/types.h>
454 +#include <time.h>
455 +#include <unistd.h>
456 +
457 +#include "doas.h"
458 +
459 +#define PERSIST_DIR "/run/doas"
460 +#define PERSIST_TIMEOUT 5 * 60
461 +
462 +static int
463 +ttyid(dev_t *tty)
464 +{
465 + int fd, i;
466 + char buf[BUFSIZ], *p;
467 + ssize_t n;
468 +
469 + fd = open("/proc/self/stat", O_RDONLY);
470 + if (fd == -1)
471 + return -1;
472 + n = read(fd, buf, sizeof(buf) - 1);
473 + if (n >= 0)
474 + buf[n] = '\0';
475 + /* check that we read the whole file */
476 + n = read(fd, buf, 1);
477 + close(fd);
478 + if (n != 0)
479 + return -1;
480 + p = strrchr(buf, ')');
481 + if (!p)
482 + return -1;
483 + ++p;
484 + /* ttr_nr is the 5th field after executable name, so skip the next 4 */
485 + for (i = 0; i < 4; ++i) {
486 + p = strchr(++p, ' ');
487 + if (!p)
488 + return -1;
489 + }
490 + *tty = strtol(p, &p, 10);
491 + if (*p != ' ')
492 + return -1;
493 + return 0;
494 +}
495 +
496 +static int
497 +persistpath(char *buf, size_t len)
498 +{
499 + dev_t tty;
500 + int n;
501 +
502 + if (ttyid(&tty) < 0)
503 + return -1;
504 + n = snprintf(buf, len, PERSIST_DIR "/%ju-%ju", (uintmax_t)getuid(), (uintmax_t)tty);
505 + if (n < 0 || n >= (int)len)
506 + return -1;
507 + return 0;
508 +}
509 +
510 +int
511 +openpersist(int *valid)
512 +{
513 + char path[256];
514 + struct stat st;
515 + struct timespec ts;
516 + int fd;
517 +
518 + if (stat(PERSIST_DIR, &st) < 0) {
519 + if (errno != ENOENT)
520 + return -1;
521 + if (mkdir(PERSIST_DIR, 0700) < 0)
522 + return -1;
523 + } else if (st.st_uid != 0 || st.st_mode != (S_IFDIR | 0700)) {
524 + return -1;
525 + }
526 + if (persistpath(path, sizeof(path)) < 0)
527 + return -1;
528 + fd = open(path, O_RDONLY);
529 + if (fd == -1) {
530 + char tmp[256];
531 + struct timespec ts[2] = { { .tv_nsec = UTIME_OMIT }, { 0 } };
532 + int n;
533 +
534 + n = snprintf(tmp, sizeof(tmp), PERSIST_DIR "/.tmp-%d", getpid());
535 + if (n < 0 || n >= (int)sizeof(tmp))
536 + return -1;
537 + fd = open(tmp, O_RDONLY | O_CREAT | O_EXCL, 0);
538 + if (fd == -1)
539 + return -1;
540 + if (futimens(fd, ts) < 0 || rename(tmp, path) < 0) {
541 + close(fd);
542 + unlink(tmp);
543 + return -1;
544 + }
545 + *valid = 0;
546 + } else {
547 + *valid = clock_gettime(CLOCK_BOOTTIME, &ts) == 0 &&
548 + fstat(fd, &st) == 0 &&
549 + (ts.tv_sec < st.st_mtim.tv_sec ||
550 + (ts.tv_sec == st.st_mtim.tv_sec && ts.tv_nsec < st.st_mtim.tv_nsec)) &&
551 + st.st_mtime - ts.tv_sec <= PERSIST_TIMEOUT;
552 + }
553 + return fd;
554 +}
555 +
556 +int
557 +setpersist(int fd)
558 +{
559 + struct timespec times[2];
560 +
561 + if (clock_gettime(CLOCK_BOOTTIME, ×[1]) < 0)
562 + return -1;
563 + times[0].tv_nsec = UTIME_OMIT;
564 + times[1].tv_sec += PERSIST_TIMEOUT;
565 + return futimens(fd, times);
566 +}
567 +
568 +int
569 +clearpersist(void)
570 +{
571 + char path[256];
572 +
573 + if (persistpath(path, sizeof(path)) < 0)
574 + return -1;
575 + if (unlink(path) < 0 && errno != ENOENT)
576 + return -1;
577 + return 0;
578 +}
579 --
580 2.49.0
581