tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Modify chpass(1) so it can set arbitrary fields



Hi,

> I'm sorry to write again, but I'm wrong again: There should have been an
> `err` instead of `errx` after the asprintf(). And some formatting of
> usage(), but as I said, it's breaking current behaviour anyway.
the attached patch enables the user to change any field he is allowed to (or
at least should be, according to the existing possibility to edit values in
EDITOR).

Regards, Julian
--- usr.bin/chpass/chpass.1
+++ usr.bin/chpass/chpass.1
@@ -46,10 +46,18 @@
 .Nm chpass
 .Op Fl a Ar list
 .Op Fl s Ar newshell
 .Op Fl y
 .Op user
+.Nm chpass
+.Op Fl f Ar option=value
+.Op Fl y
+.Op user
+.Nm chpass
+.Op Fl f Ar option=value
+.Op Fl l
+.Op user
 .Sh DESCRIPTION
 .Nm
 allows editing of the user database information associated
 with
 .Ar user
@@ -67,15 +75,25 @@
 as an argument.
 This argument must be a colon
 .Pq Dq \&:
 separated list of all the
 user database fields, although they may be empty.
+.It Fl f
+Set the field given by
+.Ar option
+to value
+.Ar value .
+If there is a space in the name of the option, you can either enquote it in
+brackets (i.e. passing still as one argument) or replace the space by an 
+underscore.
 .It Fl s
 The
 .Fl s
 option attempts to change the user's shell to
 .Ar newshell .
+This is maintained for compataibility reasons, actually it does the same as
+.Fl f Ar shell=newshell .
 .It Fl l
 This option causes the password to be updated only in the local
 password file.
 When changing only the local password,
 .Xr pwd_mkdb  8
@@ -238,10 +256,18 @@
 .It Pa /tmp/pw.XXXXXX
 Temporary copy of the user passwd information
 .It Pa /etc/shells
 The list of approved shells
 .El
+.Sh EXAMPLES
+Set home directory for user
+.Ar john
+to
+.Ar /home/john .
+This can be achieved in two different ways
+.D1 chpass Fl f Ar Home_Directory=/bin/sh Ar john
+.D1 chpass Fl f Ar \*qHome Directory=/bin/sh\*q Ar john
 .Sh SEE ALSO
 .Xr finger 1 ,
 .Xr login 1 ,
 .Xr passwd 1 ,
 .Xr pwhash 1 ,

--- usr.bin/chpass/chpass.c
+++ usr.bin/chpass/chpass.c
@@ -78,17 +78,17 @@
 __dead static void     usage(void);
 
 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 */
        arg = NULL;
 #endif
@@ -95,18 +95,23 @@
 #ifdef YP
        use_yp = _yp_check(NULL);
 #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':
                        use_yp = 0;
                        break;
@@ -180,11 +185,11 @@
        if (op == LOADENTRY && use_yp)
                errx(1, "cannot load entry using YP.\n"
                    "\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)
                                errx(1, "unknown user: %s", username);
                        if (uid && uid != pw->pw_uid)
@@ -202,16 +207,39 @@
                        err(1, "strdup");
                        /*NOTREACHED*/
                }
        }
 
-       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 && uid != pw->pw_uid)
+                       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].restricted && uid)
+                                       baduser();
+                               if (!strcmp(list[i].prompt, "shell") && uid
+                                               && !ok_shell(pw->pw_shell))
+                                       baduser();
+                               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) {
                if (uid)
                        baduser();
@@ -300,17 +328,20 @@
 static void
 usage(void)
 {
 
        (void)fprintf(stderr,
-           "usage: %s [-a list] [-s shell] [-l] [user]\n"
-           "       %s [-a list] [-s shell] [-y] [user]\n",
-           getprogname(), getprogname());
+               "usage: %s [-a list] [-s shell] [-l] [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);
 }
 
 static void
 cleanup(void)
 {
 
        (void)unlink(tempname);
 }

--- usr.bin/chpass/chpass.h
+++ usr.bin/chpass/chpass.h
@@ -33,12 +33,15 @@
 
 struct passwd;
 
 typedef struct _entry {
        const char *prompt;
-       int (*func)(const char *, struct passwd *, struct _entry *), 
restricted, len;
-       const char *except, *save;
+       int (*func)(const char *, struct passwd *, struct _entry *);
+       int restricted;
+       int len;
+       const char *except;
+       const char *save;
 } ENTRY;
 
 extern int use_yp;
 
 /* Field numbers. */

--- usr.bin/chpass/table.c
+++ usr.bin/chpass/table.c
@@ -43,10 +43,13 @@
 #include "chpass.h"
 
 char e1[] = ": ";
 char e2[] = ":,";
 
+/* Be careful when changing something here (though that might never happen...).
+ * The option names may not contain underscores, or the code in chpass.c needs
+ * to be changed! */
 ENTRY list[] = {
        { "login",              p_login,  1,   5, e1,   NULL },
        { "password",           p_passwd, 1,   8, e1,   NULL },
        { "uid",                p_uid,    1,   3, e1,   NULL },
        { "gid",                p_gid,    1,   3, e1,   NULL },

Attachment: signature.asc
Description: PGP signature



Home | Main Index | Thread Index | Old Index