logo

oasis

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