]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pw/pw.c
Add new UART device presented on newer AMT enabled systems/laptops.
[FreeBSD/FreeBSD.git] / usr.sbin / pw / pw.c
1 /*-
2  * Copyright (C) 1996
3  *      David L. Nugent.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #ifndef lint
28 static const char rcsid[] =
29   "$FreeBSD$";
30 #endif /* not lint */
31
32 #include <err.h>
33 #include <fcntl.h>
34 #include <locale.h>
35 #include <paths.h>
36 #include <stdbool.h>
37 #include <sys/wait.h>
38 #include "pw.h"
39
40 #if !defined(_PATH_YP)
41 #define _PATH_YP        "/var/yp/"
42 #endif
43 const char     *Modes[] = {
44   "add", "del", "mod", "show", "next",
45   NULL};
46 const char     *Which[] = {"user", "group", NULL};
47 static const char *Combo1[] = {
48   "useradd", "userdel", "usermod", "usershow", "usernext",
49   "lock", "unlock",
50   "groupadd", "groupdel", "groupmod", "groupshow", "groupnext",
51   NULL};
52 static const char *Combo2[] = {
53   "adduser", "deluser", "moduser", "showuser", "nextuser",
54   "lock", "unlock",
55   "addgroup", "delgroup", "modgroup", "showgroup", "nextgroup",
56   NULL};
57
58 struct pwf PWF =
59 {
60         PWF_REGULAR,
61         setpwent,
62         endpwent,
63         getpwent,
64         getpwuid,
65         getpwnam,
66         setgrent,
67         endgrent,
68         getgrent,
69         getgrgid,
70         getgrnam,
71
72 };
73 struct pwf VPWF =
74 {
75         PWF_ALT,
76         vsetpwent,
77         vendpwent,
78         vgetpwent,
79         vgetpwuid,
80         vgetpwnam,
81         vsetgrent,
82         vendgrent,
83         vgetgrent,
84         vgetgrgid,
85         vgetgrnam,
86 };
87
88 struct pwconf conf;
89
90 static struct cargs arglist;
91
92 static int      getindex(const char *words[], const char *word);
93 static void     cmdhelp(int mode, int which);
94
95
96 int
97 main(int argc, char *argv[])
98 {
99         int             ch;
100         int             mode = -1;
101         int             which = -1;
102         long            id = -1;
103         char            *config = NULL;
104         struct stat     st;
105         const char      *errstr;
106         char            arg, *name;
107         bool            relocated, nis;
108
109         static const char *opts[W_NUM][M_NUM] =
110         {
111                 { /* user */
112                         "R:V:C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y",
113                         "R:V:C:qn:u:rY",
114                         "R:V:C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:FNPY",
115                         "R:V:C:qn:u:FPa7",
116                         "R:V:C:q",
117                         "R:V:C:q",
118                         "R:V:C:q"
119                 },
120                 { /* grp  */
121                         "R:V:C:qn:g:h:H:M:opNPY",
122                         "R:V:C:qn:g:Y",
123                         "R:V:C:qn:d:g:l:h:H:FM:m:NPY",
124                         "R:V:C:qn:g:FPa",
125                         "R:V:C:q"
126                  }
127         };
128
129         static int      (*funcs[W_NUM]) (int _mode, char *_name, long _id,
130             struct cargs * _args) =
131         {                       /* Request handlers */
132                 pw_user,
133                 pw_group
134         };
135
136         name = NULL;
137         relocated = nis = false;
138         memset(&conf, 0, sizeof(conf));
139         strlcpy(conf.etcpath, _PATH_PWD, sizeof(conf.etcpath));
140
141         LIST_INIT(&arglist);
142
143         (void)setlocale(LC_ALL, "");
144
145         /*
146          * Break off the first couple of words to determine what exactly
147          * we're being asked to do
148          */
149         while (argc > 1) {
150                 int             tmp;
151
152                 if (*argv[1] == '-') {
153                         /*
154                          * Special case, allow pw -V<dir> <operation> [args] for scripts etc.
155                          */
156                         arg = argv[1][1];
157                         if (arg == 'V' || arg == 'R') {
158                                 if (relocated)
159                                         errx(EXIT_FAILURE, "Both '-R' and '-V' "
160                                             "specified, only one accepted");
161                                 relocated = true;
162                                 optarg = &argv[1][2];
163                                 if (*optarg == '\0') {
164                                         if (stat(argv[2], &st) != 0)
165                                                 errx(EX_OSFILE, \
166                                                     "no such directory `%s'",
167                                                     argv[2]);
168                                         if (!S_ISDIR(st.st_mode))
169                                                 errx(EX_OSFILE, "`%s' not a "
170                                                     "directory", argv[2]);
171                                         optarg = argv[2];
172                                         ++argv;
173                                         --argc;
174                                 }
175                                 memcpy(&PWF, &VPWF, sizeof PWF);
176                                 if (arg == 'R') {
177                                         strlcpy(conf.rootdir, optarg,
178                                             sizeof(conf.rootdir));
179                                         PWF._altdir = PWF_ROOTDIR;
180                                 }
181                                 snprintf(conf.etcpath, sizeof(conf.etcpath),
182                                     "%s%s", optarg, arg == 'R' ? "/etc" : "");
183                         } else
184                                 break;
185                 }
186                 else if (mode == -1 && (tmp = getindex(Modes, argv[1])) != -1)
187                         mode = tmp;
188                 else if (which == -1 && (tmp = getindex(Which, argv[1])) != -1)
189                         which = tmp;
190                 else if ((mode == -1 && which == -1) &&
191                          ((tmp = getindex(Combo1, argv[1])) != -1 ||
192                           (tmp = getindex(Combo2, argv[1])) != -1)) {
193                         which = tmp / M_NUM;
194                         mode = tmp % M_NUM;
195                 } else if (strcmp(argv[1], "help") == 0 && argv[2] == NULL)
196                         cmdhelp(mode, which);
197                 else if (which != -1 && mode != -1) {
198                         if (strspn(argv[1], "0123456789") == strlen(argv[1])) {
199                                 id = strtonum(argv[1], 0, LONG_MAX, &errstr);
200                                 if (errstr != NULL)
201                                         errx(EX_USAGE, "Bad id '%s': %s",
202                                             argv[1], errstr);
203                         } else
204                                 name = argv[1];
205                 } else
206                         errx(EX_USAGE, "unknown keyword `%s'", argv[1]);
207                 ++argv;
208                 --argc;
209         }
210
211         /*
212          * Bail out unless the user is specific!
213          */
214         if (mode == -1 || which == -1)
215                 cmdhelp(mode, which);
216
217         /*
218          * We know which mode we're in and what we're about to do, so now
219          * let's dispatch the remaining command line args in a genric way.
220          */
221         optarg = NULL;
222
223         while ((ch = getopt(argc, argv, opts[which][mode])) != -1) {
224                 switch (ch) {
225                 case '?':
226                         errx(EX_USAGE, "unknown switch");
227                         break;
228                 case '7':
229                         conf.v7 = true;
230                         break;
231                 case 'C':
232                         conf.config = optarg;
233                         config = conf.config;
234                         break;
235                 case 'N':
236                         conf.dryrun = true;
237                         break;
238                 case 'l':
239                         if (strlen(optarg) >= MAXLOGNAME)
240                                 errx(EX_USAGE, "new name too long: %s", optarg);
241                         conf.newname = optarg;
242                         break;
243                 case 'P':
244                         conf.pretty = true;
245                         break;
246                 case 'Y':
247                         nis = true;
248                         break;
249                 case 'g':
250                         if (which == 0) { /* for user* */
251                                 addarg(&arglist, 'g', optarg);
252                                 break;
253                         }
254                         if (strspn(optarg, "0123456789") != strlen(optarg))
255                                 errx(EX_USAGE, "-g expects a number");
256                         id = strtonum(optarg, 0, LONG_MAX, &errstr);
257                         if (errstr != NULL)
258                                 errx(EX_USAGE, "Bad id '%s': %s", optarg,
259                                     errstr);
260                         break;
261                 case 'u':
262                         if (strspn(optarg, "0123456789,") != strlen(optarg))
263                                 errx(EX_USAGE, "-u expects a number");
264                         if (strchr(optarg, ',') != NULL) {
265                                 addarg(&arglist, 'u', optarg);
266                                 break;
267                         }
268                         id = strtonum(optarg, 0, LONG_MAX, &errstr);
269                         if (errstr != NULL)
270                                 errx(EX_USAGE, "Bad id '%s': %s", optarg,
271                                     errstr);
272                         break;
273                 case 'n':
274                         if (strspn(optarg, "0123456789") != strlen(optarg)) {
275                                 name = optarg;
276                                 break;
277                         }
278                         id = strtonum(optarg, 0, LONG_MAX, &errstr);
279                         if (errstr != NULL)
280                                 errx(EX_USAGE, "Bad id '%s': %s", optarg,
281                                     errstr);
282                         break;
283                 case 'o':
284                         conf.checkduplicate = true;
285                         break;
286                 default:
287                         addarg(&arglist, ch, optarg);
288                         break;
289                 }
290                 optarg = NULL;
291         }
292
293         if (name != NULL && strlen(name) >= MAXLOGNAME)
294                 errx(EX_USAGE, "name too long: %s", name);
295
296         /*
297          * Must be root to attempt an update
298          */
299         if (geteuid() != 0 && mode != M_PRINT && mode != M_NEXT && !conf.dryrun)
300                 errx(EX_NOPERM, "you must be root to run this program");
301
302         /*
303          * We should immediately look for the -q 'quiet' switch so that we
304          * don't bother with extraneous errors
305          */
306         if (getarg(&arglist, 'q') != NULL)
307                 freopen(_PATH_DEVNULL, "w", stderr);
308
309         /*
310          * Set our base working path if not overridden
311          */
312
313         if (config == NULL) {   /* Only override config location if -C not specified */
314                 asprintf(&config, "%s/pw.conf", conf.etcpath);
315                 if (config == NULL)
316                         errx(EX_OSERR, "out of memory");
317         }
318
319         /*
320          * Now, let's do the common initialisation
321          */
322         conf.userconf = read_userconfig(config);
323
324         ch = funcs[which] (mode, name, id, &arglist);
325
326         /*
327          * If everything went ok, and we've been asked to update
328          * the NIS maps, then do it now
329          */
330         if (ch == EXIT_SUCCESS && nis) {
331                 pid_t   pid;
332
333                 fflush(NULL);
334                 if (chdir(_PATH_YP) == -1)
335                         warn("chdir(" _PATH_YP ")");
336                 else if ((pid = fork()) == -1)
337                         warn("fork()");
338                 else if (pid == 0) {
339                         /* Is make anywhere else? */
340                         execlp("/usr/bin/make", "make", (char *)NULL);
341                         _exit(1);
342                 } else {
343                         int   i;
344                         waitpid(pid, &i, 0);
345                         if ((i = WEXITSTATUS(i)) != 0)
346                                 errx(ch, "make exited with status %d", i);
347                         else
348                                 pw_log(conf.userconf, mode, which, "NIS maps updated");
349                 }
350         }
351         return ch;
352 }
353
354
355 static int
356 getindex(const char *words[], const char *word)
357 {
358         int             i = 0;
359
360         while (words[i]) {
361                 if (strcmp(words[i], word) == 0)
362                         return i;
363                 i++;
364         }
365         return -1;
366 }
367
368
369 /*
370  * This is probably an overkill for a cmdline help system, but it reflects
371  * the complexity of the command line.
372  */
373
374 static void
375 cmdhelp(int mode, int which)
376 {
377         if (which == -1)
378                 fprintf(stderr, "usage:\n  pw [user|group|lock|unlock] [add|del|mod|show|next] [help|switches/values]\n");
379         else if (mode == -1)
380                 fprintf(stderr, "usage:\n  pw %s [add|del|mod|show|next] [help|switches/values]\n", Which[which]);
381         else {
382
383                 /*
384                  * We need to give mode specific help
385                  */
386                 static const char *help[W_NUM][M_NUM] =
387                 {
388                         {
389                                 "usage: pw useradd [name] [switches]\n"
390                                 "\t-V etcdir      alternate /etc location\n"
391                                 "\t-R rootir      alternate root directory\n"
392                                 "\t-C config      configuration file\n"
393                                 "\t-q             quiet operation\n"
394                                 "  Adding users:\n"
395                                 "\t-n name        login name\n"
396                                 "\t-u uid         user id\n"
397                                 "\t-c comment     user name/comment\n"
398                                 "\t-d directory   home directory\n"
399                                 "\t-e date        account expiry date\n"
400                                 "\t-p date        password expiry date\n"
401                                 "\t-g grp         initial group\n"
402                                 "\t-G grp1,grp2   additional groups\n"
403                                 "\t-m [ -k dir ]  create and set up home\n"
404                                 "\t-M mode        home directory permissions\n"
405                                 "\t-s shell       name of login shell\n"
406                                 "\t-o             duplicate uid ok\n"
407                                 "\t-L class       user class\n"
408                                 "\t-h fd          read password on fd\n"
409                                 "\t-H fd          read encrypted password on fd\n"
410                                 "\t-Y             update NIS maps\n"
411                                 "\t-N             no update\n"
412                                 "  Setting defaults:\n"
413                                 "\t-V etcdir      alternate /etc location\n"
414                                 "\t-R rootir      alternate root directory\n"
415                                 "\t-D             set user defaults\n"
416                                 "\t-b dir         default home root dir\n"
417                                 "\t-e period      default expiry period\n"
418                                 "\t-p period      default password change period\n"
419                                 "\t-g group       default group\n"
420                                 "\t-G grp1,grp2   additional groups\n"
421                                 "\t-L class       default user class\n"
422                                 "\t-k dir         default home skeleton\n"
423                                 "\t-M mode        home directory permissions\n"
424                                 "\t-u min,max     set min,max uids\n"
425                                 "\t-i min,max     set min,max gids\n"
426                                 "\t-w method      set default password method\n"
427                                 "\t-s shell       default shell\n"
428                                 "\t-y path        set NIS passwd file path\n",
429                                 "usage: pw userdel [uid|name] [switches]\n"
430                                 "\t-V etcdir      alternate /etc location\n"
431                                 "\t-R rootir      alternate root directory\n"
432                                 "\t-n name        login name\n"
433                                 "\t-u uid         user id\n"
434                                 "\t-Y             update NIS maps\n"
435                                 "\t-r             remove home & contents\n",
436                                 "usage: pw usermod [uid|name] [switches]\n"
437                                 "\t-V etcdir      alternate /etc location\n"
438                                 "\t-R rootir      alternate root directory\n"
439                                 "\t-C config      configuration file\n"
440                                 "\t-q             quiet operation\n"
441                                 "\t-F             force add if no user\n"
442                                 "\t-n name        login name\n"
443                                 "\t-u uid         user id\n"
444                                 "\t-c comment     user name/comment\n"
445                                 "\t-d directory   home directory\n"
446                                 "\t-e date        account expiry date\n"
447                                 "\t-p date        password expiry date\n"
448                                 "\t-g grp         initial group\n"
449                                 "\t-G grp1,grp2   additional groups\n"
450                                 "\t-l name        new login name\n"
451                                 "\t-L class       user class\n"
452                                 "\t-m [ -k dir ]  create and set up home\n"
453                                 "\t-M mode        home directory permissions\n"
454                                 "\t-s shell       name of login shell\n"
455                                 "\t-w method      set new password using method\n"
456                                 "\t-h fd          read password on fd\n"
457                                 "\t-H fd          read encrypted password on fd\n"
458                                 "\t-Y             update NIS maps\n"
459                                 "\t-N             no update\n",
460                                 "usage: pw usershow [uid|name] [switches]\n"
461                                 "\t-V etcdir      alternate /etc location\n"
462                                 "\t-R rootir      alternate root directory\n"
463                                 "\t-n name        login name\n"
464                                 "\t-u uid         user id\n"
465                                 "\t-F             force print\n"
466                                 "\t-P             prettier format\n"
467                                 "\t-a             print all users\n"
468                                 "\t-7             print in v7 format\n",
469                                 "usage: pw usernext [switches]\n"
470                                 "\t-V etcdir      alternate /etc location\n"
471                                 "\t-R rootir      alternate root directory\n"
472                                 "\t-C config      configuration file\n"
473                                 "\t-q             quiet operation\n",
474                                 "usage pw: lock [switches]\n"
475                                 "\t-V etcdir      alternate /etc locations\n"
476                                 "\t-C config      configuration file\n"
477                                 "\t-q             quiet operation\n",
478                                 "usage pw: unlock [switches]\n"
479                                 "\t-V etcdir      alternate /etc locations\n"
480                                 "\t-C config      configuration file\n"
481                                 "\t-q             quiet operation\n"
482                         },
483                         {
484                                 "usage: pw groupadd [group|gid] [switches]\n"
485                                 "\t-V etcdir      alternate /etc location\n"
486                                 "\t-R rootir      alternate root directory\n"
487                                 "\t-C config      configuration file\n"
488                                 "\t-q             quiet operation\n"
489                                 "\t-n group       group name\n"
490                                 "\t-g gid         group id\n"
491                                 "\t-M usr1,usr2   add users as group members\n"
492                                 "\t-o             duplicate gid ok\n"
493                                 "\t-Y             update NIS maps\n"
494                                 "\t-N             no update\n",
495                                 "usage: pw groupdel [group|gid] [switches]\n"
496                                 "\t-V etcdir      alternate /etc location\n"
497                                 "\t-R rootir      alternate root directory\n"
498                                 "\t-n name        group name\n"
499                                 "\t-g gid         group id\n"
500                                 "\t-Y             update NIS maps\n",
501                                 "usage: pw groupmod [group|gid] [switches]\n"
502                                 "\t-V etcdir      alternate /etc location\n"
503                                 "\t-R rootir      alternate root directory\n"
504                                 "\t-C config      configuration file\n"
505                                 "\t-q             quiet operation\n"
506                                 "\t-F             force add if not exists\n"
507                                 "\t-n name        group name\n"
508                                 "\t-g gid         group id\n"
509                                 "\t-M usr1,usr2   replaces users as group members\n"
510                                 "\t-m usr1,usr2   add users as group members\n"
511                                 "\t-d usr1,usr2   delete users as group members\n"
512                                 "\t-l name        new group name\n"
513                                 "\t-Y             update NIS maps\n"
514                                 "\t-N             no update\n",
515                                 "usage: pw groupshow [group|gid] [switches]\n"
516                                 "\t-V etcdir      alternate /etc location\n"
517                                 "\t-R rootir      alternate root directory\n"
518                                 "\t-n name        group name\n"
519                                 "\t-g gid         group id\n"
520                                 "\t-F             force print\n"
521                                 "\t-P             prettier format\n"
522                                 "\t-a             print all accounting groups\n",
523                                 "usage: pw groupnext [switches]\n"
524                                 "\t-V etcdir      alternate /etc location\n"
525                                 "\t-R rootir      alternate root directory\n"
526                                 "\t-C config      configuration file\n"
527                                 "\t-q             quiet operation\n"
528                         }
529                 };
530
531                 fprintf(stderr, "%s", help[which][mode]);
532         }
533         exit(EXIT_FAILURE);
534 }
535
536 struct carg    *
537 getarg(struct cargs * _args, int ch)
538 {
539         struct carg    *c = LIST_FIRST(_args);
540
541         while (c != NULL && c->ch != ch)
542                 c = LIST_NEXT(c, list);
543         return c;
544 }
545
546 struct carg    *
547 addarg(struct cargs * _args, int ch, char *argstr)
548 {
549         struct carg    *ca = malloc(sizeof(struct carg));
550
551         if (ca == NULL)
552                 errx(EX_OSERR, "out of memory");
553         ca->ch = ch;
554         ca->val = argstr;
555         LIST_INSERT_HEAD(_args, ca, list);
556         return ca;
557 }