Hi, > + op = SINGLEENTRY; > + arg = calloc(1, sizeof(char)*(7 + strlen(optarg))); > + arg = strcpy(arg, "shell:"); > + arg = strncat(arg, optarg, sizeof(arg) - 7); > + break; > > First, you could use asprintf(3) instead of the calloc/string dance. > > Second, unless mistaken, are you sure that your strncat() call is > correct? sizeof(arg) - 7 looks like a very nice overflow to me, at least > for anything that is 32 bits or lower. And for 64 bits, this would just > copy 1 byte. of course you're right. I know I had asprintf here first, and then an PATH_MAX array, but then changed it... Attached is a version with asprintf. Anyway, I just remembered this change is not possible anyway as users couldn't change their login shells anymore, which is the current behaviour. Regards, Julian
Index: chpass.c =================================================================== RCS file: /cvsroot/src/usr.bin/chpass/chpass.c,v retrieving revision 1.35 diff -u -r1.35 chpass.c --- chpass.c 31 Aug 2011 16:24:57 -0000 1.35 +++ chpass.c 28 Nov 2011 21:31:38 -0000 @@ -80,13 +80,13 @@ int main(int argc, char **argv) { - enum { NEWSH, LOADENTRY, EDITENTRY } op; + enum { LOADENTRY, EDITENTRY, SINGLEENTRY } op; struct passwd *pw, lpw, old_pw; - int ch, dfd, pfd, tfd; + int ch, dfd, pfd, tfd, i, ffound; #ifdef YP int yflag = 0; #endif - char *arg, *username = NULL; + char *arg, *opt, *username = NULL; #ifdef __GNUC__ pw = NULL; /* XXX gcc -Wuninitialized */ @@ -97,14 +97,19 @@ #endif op = EDITENTRY; - while ((ch = getopt(argc, argv, "a:s:ly")) != -1) + while ((ch = getopt(argc, argv, "f:a:s:ly")) != -1) switch (ch) { case 'a': op = LOADENTRY; arg = optarg; break; case 's': - op = NEWSH; + op = SINGLEENTRY; + if (asprintf(&arg, "shell:%s", optarg) <= 0) + errx(1, "asprintf"); + break; + case 'f': + op = SINGLEENTRY; arg = optarg; break; case 'l': @@ -182,7 +187,7 @@ "\tUse the -l flag to load local."); #endif - if (op == EDITENTRY || op == NEWSH) { + if (op == EDITENTRY || op == SINGLEENTRY) { if (username != NULL) { pw = getpwnam(username); if (pw == NULL) @@ -204,12 +209,30 @@ } } - if (op == NEWSH) { - /* protect p_shell -- it thinks NULL is /bin/sh */ - if (!arg[0]) + if (op == SINGLEENTRY) { + /* Separate option and value. */ + opt = strsep(&arg, ":"); + if (opt == NULL || !strlen(opt) | !strlen(arg)) usage(); - if (p_shell(arg, pw, NULL)) - (*Pw_error)(NULL, 0, 1); + if (uid) // XXX: On the long run, let users change their own entries. + baduser(); + + /* The option name has a space replaced by underscore. */ + if (strchr(opt, '_')) + *(strchr(opt, '_')) = ' '; + + /* Search for the right field. */ + ffound = 0; + for (i = 0; list[i].prompt != NULL; i++) + if (!strcasecmp(opt, list[i].prompt)) { + if (list[i].func(arg, pw, &list[i])) + (*Pw_error)(NULL, 0, 1); + ffound = 1; + break; + } + + if (!ffound) + errx(1, "invalid field specification: %s", opt); } if (op == LOADENTRY) { @@ -303,7 +326,10 @@ (void)fprintf(stderr, "usage: %s [-a list] [-s shell] [-l] [user]\n" - " %s [-a list] [-s shell] [-y] [user]\n", + " %s [-a list] [-s shell] [-y] [user]\n" + " %s [-f option:value] [-y] [user]\n" + " %s [-f option:value] [-l] [user]\n", + getprogname(), getprogname(), getprogname(), getprogname()); exit(1); }
Attachment:
signature.asc
Description: PGP signature