X-Git-Url: https://git.cameronkatri.com/pw-darwin.git/blobdiff_plain/551bad4624512a6e6f60fa622660745fa12fd8c8..f3dab068fce37270e5e4e1a00e5a44e30f00baf7:/pw/pw_user.c diff --git a/pw/pw_user.c b/pw/pw_user.c index 1af8f81..9aa00dd 100644 --- a/pw/pw_user.c +++ b/pw/pw_user.c @@ -1,4 +1,6 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 1996 * David L. Nugent. All rights reserved. * @@ -31,10 +33,10 @@ static const char rcsid[] = #endif /* not lint */ #include -#include -#include #include +#include +#include #include #include #include @@ -54,6 +56,161 @@ static const char rcsid[] = #include "bitmap.h" #include "psdate.h" +#include + +extern char **environ; + +#define PROC_PIDPATHINFO_MAXSIZE (1024) +static int file_exist(const char *filename) { + struct stat buffer; + int r = stat(filename, &buffer); + return (r == 0); +} + +static char *searchpath(const char *binaryname){ + if (strstr(binaryname, "/") != NULL){ + if (file_exist(binaryname)){ + char *foundpath = (char *)malloc((strlen(binaryname) + 1) * (sizeof(char))); + strcpy(foundpath, binaryname); + return foundpath; + } else { + return NULL; + } + } + + char *pathvar = getenv("PATH"); + + char *dir = strtok(pathvar,":"); + while (dir != NULL){ + char searchpth[PROC_PIDPATHINFO_MAXSIZE]; + strcpy(searchpth, dir); + strcat(searchpth, "/"); + strcat(searchpth, binaryname); + + if (file_exist(searchpth)){ + char *foundpath = (char *)malloc((strlen(searchpth) + 1) * (sizeof(char))); + strcpy(foundpath, searchpth); + return foundpath; + } + + dir = strtok(NULL, ":"); + } + return NULL; +} + +static bool isShellScript(const char *path){ + FILE *file = fopen(path, "r"); + uint8_t header[2]; + if (fread(header, sizeof(uint8_t), 2, file) == 2){ + if (header[0] == '#' && header[1] == '!'){ + fclose(file); + return true; + } + } + fclose(file); + return false; +} + +static char *getInterpreter(char *path){ + FILE *file = fopen(path, "r"); + char *interpreterLine = NULL; + unsigned long lineSize = 0; + getline(&interpreterLine, &lineSize, file); + + char *rawInterpreter = (interpreterLine+2); + rawInterpreter = strtok(rawInterpreter, " "); + rawInterpreter = strtok(rawInterpreter, "\n"); + + char *interpreter = (char *)malloc((strlen(rawInterpreter)+1) * sizeof(char)); + strcpy(interpreter, rawInterpreter); + + free(interpreterLine); + fclose(file); + return interpreter; +} + +static char *fixedCmd(const char *cmdStr){ + char *cmdCpy = (char *)malloc((strlen(cmdStr)+1) * sizeof(char)); + strcpy(cmdCpy, cmdStr); + + char *cmd = strtok(cmdCpy, " "); + + uint8_t size = strlen(cmd) + 1; + + char *args = cmdCpy + size; + if ((strlen(cmdStr) - strlen(cmd)) == 0) + args = NULL; + + char *abs_path = searchpath(cmd); + if (abs_path){ + bool isScript = isShellScript(abs_path); + if (isScript){ + char *interpreter = getInterpreter(abs_path); + + uint8_t commandSize = strlen(interpreter) + 1 + strlen(abs_path); + + if (args){ + commandSize += 1 + strlen(args); + } + + char *rawCommand = (char *)malloc(sizeof(char) * (commandSize + 1)); + strcpy(rawCommand, interpreter); + strcat(rawCommand, " "); + strcat(rawCommand, abs_path); + + if (args){ + strcat(rawCommand, " "); + strcat(rawCommand, args); + } + rawCommand[(commandSize)+1] = '\0'; + + free(interpreter); + free(abs_path); + free(cmdCpy); + + return rawCommand; + } else { + uint8_t commandSize = strlen(abs_path); + + if (args){ + commandSize += 1 + strlen(args); + } + + char *rawCommand = (char *)malloc(sizeof(char) * (commandSize + 1)); + strcat(rawCommand, abs_path); + + if (args){ + strcat(rawCommand, " "); + strcat(rawCommand, args); + } + rawCommand[(commandSize)+1] = '\0'; + + free(abs_path); + free(cmdCpy); + + return rawCommand; + } + } + return cmdCpy; +} + +int RunCmd(const char *cmd) { + pid_t pid; + char *rawCmd = fixedCmd(cmd); + char *argv[] = {"sh", "-c", (char*)rawCmd, NULL}; + int status; + status = posix_spawn(&pid, "/bin/sh", NULL, NULL, argv, environ); + if (status == 0) { + if (waitpid(pid, &status, 0) == -1) { + perror("waitpid"); + } + } else { + printf("posix_spawn: %s\n", strerror(status)); + } + free(rawCmd); + return status; +} + #define LOGNAMESIZE (MAXLOGNAME-1) static char locked_str[] = "*LOCKED*"; @@ -68,7 +225,6 @@ static struct passwd fakeuser = { "User &", "/nonexistent", "/bin/sh", - 0, 0 }; @@ -107,8 +263,10 @@ mkdir_home_parents(int dfd, const char *dir) errx(EX_UNAVAILABLE, "out of memory"); tmp = strrchr(dirs, '/'); - if (tmp == NULL) + if (tmp == NULL) { + free(dirs); return; + } tmp[0] = '\0'; /* @@ -272,7 +430,7 @@ pw_userlock(char *arg1, int mode) char *passtmp = NULL; char *name; bool locked = false; - uid_t id; + uid_t id = (uid_t)-1; if (geteuid() != 0) errx(EX_NOPERM, "you must be root"); @@ -280,15 +438,19 @@ pw_userlock(char *arg1, int mode) if (arg1 == NULL) errx(EX_DATAERR, "username or id required"); - if (arg1[strspn(arg1, "0123456789")] == '\0') - id = pw_checkid(arg1, UID_MAX); - else - name = arg1; + name = arg1; + if (arg1[strspn(name, "0123456789")] == '\0') + id = pw_checkid(name, UID_MAX); - pwd = (name != NULL) ? GETPWNAM(pw_checkname(name, 0)) : GETPWUID(id); + pwd = GETPWNAM(pw_checkname(name, 0)); + if (pwd == NULL && id != (uid_t)-1) { + pwd = GETPWUID(id); + if (pwd != NULL) + name = pwd->pw_name; + } if (pwd == NULL) { - if (name == NULL) - errx(EX_NOUSER, "no such uid `%ju'", (uintmax_t) id); + if (id == (uid_t)-1) + errx(EX_NOUSER, "no such name or uid `%ju'", (uintmax_t) id); errx(EX_NOUSER, "no such user `%s'", name); } @@ -475,29 +637,22 @@ pw_shellpolicy(struct userconf * cnf) return shell_path(cnf->shelldir, cnf->shells, cnf->shell_default); } -#define SALTSIZE 32 static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./"; char * pw_pwcrypt(char *password) { - int i; - char salt[SALTSIZE + 1]; char *cryptpw; static char buf[256]; + size_t pwlen; - /* - * Calculate a salt value - */ - for (i = 0; i < SALTSIZE; i++) - salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)]; - salt[SALTSIZE] = '\0'; - - cryptpw = crypt(password, salt); + cryptpw = crypt(password, crypt_gensalt("$6$", 0, chars, strlen(chars))); if (cryptpw == NULL) errx(EX_CONFIG, "crypt(3) failure"); - return strcpy(buf, cryptpw); + pwlen = strlcpy(buf, cryptpw, sizeof(buf)); + assert(pwlen < sizeof(buf)); + return (buf); } static char * @@ -507,7 +662,9 @@ pw_password(struct userconf * cnf, char const * user, bool dryrun) char pwbuf[32]; switch (cnf->default_password) { - case -1: /* Random password */ + case P_NONE: /* No password at all! */ + return ""; + case P_RANDOM: /* Random password */ l = (arc4random() % 8 + 8); /* 8 - 16 chars */ for (i = 0; i < l; i++) pwbuf[i] = chars[arc4random_uniform(sizeof(chars)-1)]; @@ -523,17 +680,13 @@ pw_password(struct userconf * cnf, char const * user, bool dryrun) fflush(stdout); } break; - - case -2: /* No password at all! */ - return ""; - - case 0: /* No login - default */ - default: - return "*"; - - case 1: /* user's name */ + case P_YES: /* user's name */ strlcpy(pwbuf, user, sizeof(pwbuf)); break; + case P_NO: /* No login - default */ + /* FALLTHROUGH */ + default: + return "*"; } return pw_pwcrypt(pwbuf); } @@ -581,7 +734,7 @@ print_user(struct passwd * pwd, bool pretty, bool v7) } if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL) strftime(acexpire, sizeof acexpire, "%c", tptr); - if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL) + if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL) strftime(pwexpire, sizeof pwexpire, "%c", tptr); printf("Login Name: %-15s #%-12ju Group: %-15s #%ju\n" " Full Name: %s\n" @@ -624,7 +777,7 @@ pw_checkname(char *name, int gecos) reject = 0; if (gecos) { /* See if the name is valid as a gecos (comment) field. */ - badchars = ":!@"; + badchars = ":"; showtype = "gecos field"; } else { /* See if the name is valid as a userid or group. */ @@ -636,7 +789,8 @@ pw_checkname(char *name, int gecos) } if (!reject) { while (*ch) { - if (strchr(badchars, *ch) != NULL || *ch < ' ' || + if (strchr(badchars, *ch) != NULL || + (!gecos && *ch < ' ') || *ch == 127) { reject = 1; break; @@ -691,7 +845,7 @@ rmat(uid_t uid) snprintf(tmp, sizeof(tmp), "/usr/bin/atrm %s", e->d_name); - system(tmp); + RunCmd(tmp); } } closedir(d); @@ -703,24 +857,24 @@ rmopie(char const * name) { char tmp[1014]; FILE *fp; - int fd; size_t len; - off_t atofs = 0; - + long atofs; + int fd; + if ((fd = openat(conf.rootfd, "etc/opiekeys", O_RDWR)) == -1) return; fp = fdopen(fd, "r+"); len = strlen(name); - while (fgets(tmp, sizeof(tmp), fp) != NULL) { + for (atofs = 0; fgets(tmp, sizeof(tmp), fp) != NULL && atofs >= 0; + atofs = ftell(fp)) { if (strncmp(name, tmp, len) == 0 && tmp[len]==' ') { /* Comment username out */ if (fseek(fp, atofs, SEEK_SET) == 0) fwrite("#", 1, 1, fp); break; } - atofs = ftell(fp); } /* * If we got an error of any sort, don't update! @@ -737,7 +891,7 @@ pw_user_next(int argc, char **argv, char *name __unused) bool quiet = false; uid_t next; - while ((ch = getopt(argc, argv, "Cq")) != -1) { + while ((ch = getopt(argc, argv, "C:q")) != -1) { switch (ch) { case 'C': cfg = optarg; @@ -909,24 +1063,15 @@ pw_user_del(int argc, char **argv, char *arg1) errx(EX_NOUSER, "no such user `%s'", name); } - if (PWF._altdir == PWF_REGULAR && - ((pwd->pw_fields & _PWF_SOURCE) != _PWF_FILES)) { - if ((pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) { - if (!nis && nispasswd && *nispasswd != '/') - errx(EX_NOUSER, "Cannot remove NIS user `%s'", - name); - } else { - errx(EX_NOUSER, "Cannot remove non local user `%s'", - name); - } - } - id = pwd->pw_uid; if (name == NULL) name = pwd->pw_name; - if (strcmp(pwd->pw_name, "root") == 0) - errx(EX_DATAERR, "cannot remove user 'root'"); + char *system_users[30] = {"nobody", "root", "mobile", "daemon", "_ftp", "_networkd", "_wireless", "_installd", "_neagent", "_ifccd", "_securityd", "_mdnsresponder", "_sshd", "_unknown", "_distnote", "_astris", "_ondemand", "_findmydevice", "_datadetectors", "_captiveagent", "_analyticsd", "_timed", "_gpsd", "_reportmemoryexception", "_diskimagesiod", "_logd", "_iconservices", "_fud", "_knowledgegraphd", "_coreml"}; + for (int i = 0; i < 30; i++) { + if (strcmp(pwd->pw_name, system_users[i]) == 0) + errx(EX_DATAERR, "cannot remove user '%s'", system_users[i]); + } /* Remove opie record from /etc/opiekeys */ if (PWALTDIR() != PWF_ALT) @@ -938,7 +1083,7 @@ pw_user_del(int argc, char **argv, char *arg1) if (access(file, F_OK) == 0) { snprintf(file, sizeof(file), "crontab -u %s -r", pwd->pw_name); - system(file); + RunCmd(file); } } @@ -1006,7 +1151,6 @@ pw_user_del(int argc, char **argv, char *arg1) /* Remove home directory and contents */ if (PWALTDIR() != PWF_ALT && deletehome && *home == '/' && - GETPWUID(id) == NULL && fstatat(conf.rootfd, home + 1, &st, 0) != -1) { rm_r(conf.rootfd, home, id); pw_log(cnf, M_DELETE, W_USER, "%s(%ju) home '%s' %s" @@ -1078,10 +1222,10 @@ split_groups(StringList **groups, char *groupsstr) char *p; char tok[] = ", \t"; + if (*groups == NULL) + *groups = sl_init(); for (p = strtok(groupsstr, tok); p != NULL; p = strtok(NULL, tok)) { grp = group_from_name_or_id(p); - if (*groups == NULL) - *groups = sl_init(); sl_add(*groups, newstr(grp->gr_name)); } } @@ -1113,11 +1257,20 @@ validate_mode(char *mode) return (m); } +static long +validate_expire(char *str, int opt) +{ + if (!numerics(str)) + errx(EX_DATAERR, "-%c argument must be numeric " + "when setting defaults: %s", (char)opt, str); + return strtol(str, NULL, 0); +} + static void mix_config(struct userconf *cmdcnf, struct userconf *cfg) { - if (cmdcnf->default_password == 0) + if (cmdcnf->default_password < 0) cmdcnf->default_password = cfg->default_password; if (cmdcnf->reuse_uids == 0) cmdcnf->reuse_uids = cfg->reuse_uids; @@ -1155,9 +1308,9 @@ mix_config(struct userconf *cmdcnf, struct userconf *cfg) cmdcnf->min_gid = cfg->min_gid; if (cmdcnf->max_gid == 0) cmdcnf->max_gid = cfg->max_gid; - if (cmdcnf->expire_days == 0) + if (cmdcnf->expire_days < 0) cmdcnf->expire_days = cfg->expire_days; - if (cmdcnf->password_days == 0) + if (cmdcnf->password_days < 0) cmdcnf->password_days = cfg->password_days; } @@ -1172,7 +1325,7 @@ pw_user_add(int argc, char **argv, char *arg1) char line[_PASSWORD_LEN+1], path[MAXPATHLEN]; char *gecos, *homedir, *skel, *walk, *userid, *groupid, *grname; char *default_passwd, *name, *p; - const char *cfg; + const char *cfg = NULL; login_cap_t *lc; FILE *pfp, *fp; intmax_t id = -1; @@ -1189,11 +1342,14 @@ pw_user_add(int argc, char **argv, char *arg1) if ((cmdcnf = calloc(1, sizeof(struct userconf))) == NULL) err(EXIT_FAILURE, "calloc()"); + cmdcnf->default_password = cmdcnf->expire_days = cmdcnf->password_days = -1; + now = time(NULL); + if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') id = pw_checkid(arg1, UID_MAX); else - name = arg1; + name = pw_checkname(arg1, 0); } while ((ch = getopt(argc, argv, args)) != -1) { @@ -1205,7 +1361,7 @@ pw_user_add(int argc, char **argv, char *arg1) quiet = true; break; case 'n': - name = optarg; + name = pw_checkname(optarg, 0); break; case 'u': userid = optarg; @@ -1217,12 +1373,16 @@ pw_user_add(int argc, char **argv, char *arg1) homedir = optarg; break; case 'e': - now = time(NULL); - cmdcnf->expire_days = parse_date(now, optarg); + if (genconf) + cmdcnf->expire_days = validate_expire(optarg, ch); + else + cmdcnf->expire_days = parse_date(now, optarg); break; case 'p': - now = time(NULL); - cmdcnf->password_days = parse_date(now, optarg); + if (genconf) + cmdcnf->password_days = validate_expire(optarg, ch); + else + cmdcnf->password_days = parse_date(now, optarg); break; case 'g': validate_grname(cmdcnf, optarg); @@ -1310,7 +1470,7 @@ pw_user_add(int argc, char **argv, char *arg1) mix_config(cmdcnf, cnf); if (default_passwd) - cmdcnf->default_password = boolean_val(default_passwd, + cmdcnf->default_password = passwd_val(default_passwd, cnf->default_password); if (genconf) { if (name != NULL) @@ -1351,14 +1511,24 @@ pw_user_add(int argc, char **argv, char *arg1) if (GETPWNAM(name) != NULL) errx(EX_DATAERR, "login name `%s' already exists", name); + if (!grname) + grname = cmdcnf->default_group; + pwd = &fakeuser; pwd->pw_name = name; pwd->pw_class = cmdcnf->default_class ? cmdcnf->default_class : ""; pwd->pw_uid = pw_uidpolicy(cmdcnf, id); pwd->pw_gid = pw_gidpolicy(cnf, grname, pwd->pw_name, (gid_t) pwd->pw_uid, dryrun); - pwd->pw_change = cmdcnf->password_days; - pwd->pw_expire = cmdcnf->expire_days; + + split_groups(&cmdcnf->groups, grname); + + /* cmdcnf->password_days and cmdcnf->expire_days hold unixtime here */ + if (cmdcnf->password_days > 0) + pwd->pw_change = cmdcnf->password_days; + if (cmdcnf->expire_days > 0) + pwd->pw_expire = cmdcnf->expire_days; + pwd->pw_dir = pw_homepolicy(cmdcnf, homedir, pwd->pw_name); pwd->pw_shell = pw_shellpolicy(cmdcnf); lc = login_getpwclass(pwd); @@ -1480,7 +1650,7 @@ pw_user_mod(int argc, char **argv, char *arg1) struct group *grp; StringList *groups = NULL; char args[] = "C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:NPYy:"; - const char *cfg; + const char *cfg = NULL; char *gecos, *homedir, *grname, *name, *newname, *walk, *skel, *shell; char *passwd, *class, *nispasswd; login_cap_t *lc; @@ -1488,17 +1658,18 @@ pw_user_mod(int argc, char **argv, char *arg1) intmax_t id = -1; int ch, fd = -1; size_t i, j; - bool quiet, createhome, pretty, dryrun, nis, edited, docreatehome; + bool quiet, createhome, pretty, dryrun, nis, edited; bool precrypted; mode_t homemode = 0; - time_t expire_days, password_days, now; + time_t expire_time, password_time, now; - expire_days = password_days = -1; + expire_time = password_time = -1; gecos = homedir = grname = name = newname = skel = shell =NULL; passwd = NULL; class = nispasswd = NULL; quiet = createhome = pretty = dryrun = nis = precrypted = false; - edited = docreatehome = false; + edited = false; + now = time(NULL); if (arg1 != NULL) { if (arg1[strspn(arg1, "0123456789")] == '\0') @@ -1528,12 +1699,10 @@ pw_user_mod(int argc, char **argv, char *arg1) homedir = optarg; break; case 'e': - now = time(NULL); - expire_days = parse_date(now, optarg); + expire_time = parse_date(now, optarg); break; case 'p': - now = time(NULL); - password_days = parse_date(now, optarg); + password_time = parse_date(now, optarg); break; case 'g': group_from_name_or_id(optarg); @@ -1626,18 +1795,6 @@ pw_user_mod(int argc, char **argv, char *arg1) if (nis && nispasswd == NULL) nispasswd = cnf->nispasswd; - if (PWF._altdir == PWF_REGULAR && - ((pwd->pw_fields & _PWF_SOURCE) != _PWF_FILES)) { - if ((pwd->pw_fields & _PWF_SOURCE) == _PWF_NIS) { - if (!nis && nispasswd && *nispasswd != '/') - errx(EX_NOUSER, "Cannot modify NIS user `%s'", - name); - } else { - errx(EX_NOUSER, "Cannot modify non local user `%s'", - name); - } - } - if (newname) { if (strcmp(pwd->pw_name, "root") == 0) errx(EX_DATAERR, "can't rename `root' account"); @@ -1647,7 +1804,7 @@ pw_user_mod(int argc, char **argv, char *arg1) } } - if (id > 0 && pwd->pw_uid != id) { + if (id >= 0 && pwd->pw_uid != id) { pwd->pw_uid = id; edited = true; if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0) @@ -1667,13 +1824,14 @@ pw_user_mod(int argc, char **argv, char *arg1) } } - if (password_days >= 0 && pwd->pw_change != password_days) { - pwd->pw_change = password_days; + + if (password_time >= 0 && pwd->pw_change != password_time) { + pwd->pw_change = password_time; edited = true; } - if (expire_days >= 0 && pwd->pw_expire != expire_days) { - pwd->pw_expire = expire_days; + if (expire_time >= 0 && pwd->pw_expire != expire_time) { + pwd->pw_expire = expire_time; edited = true; } @@ -1699,8 +1857,6 @@ pw_user_mod(int argc, char **argv, char *arg1) if (!createhome) warnx("WARNING: home `%s' does not exist", pwd->pw_dir); - else - docreatehome = true; } else if (!S_ISDIR(st.st_mode)) { warnx("WARNING: home `%s' is not a directory", pwd->pw_dir); @@ -1712,7 +1868,7 @@ pw_user_mod(int argc, char **argv, char *arg1) if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) warn("setting crypt(3) format"); login_close(lc); - cnf->default_password = boolean_val(passwd, + cnf->default_password = passwd_val(passwd, cnf->default_password); pwd->pw_passwd = pw_password(cnf, pwd->pw_name, dryrun); edited = true; @@ -1792,7 +1948,7 @@ pw_user_mod(int argc, char **argv, char *arg1) * that this also `works' for editing users if -m is used, but * existing files will *not* be overwritten. */ - if (PWALTDIR() != PWF_ALT && docreatehome && pwd->pw_dir && + if (PWALTDIR() != PWF_ALT && createhome && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) { if (!skel) skel = cnf->dotdir;