1 /*        $NetBSD: passwd.c,v 1.32 2017/10/12 05:00:23 ryo Exp $      */
2 
3 /*
4  * Copyright (c) 1988, 1993, 1994
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994\
35  The Regents of the University of California.  All rights reserved.");
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "from: @(#)passwd.c    8.3 (Berkeley) 4/2/94";
41 #else
42 __RCSID("$NetBSD: passwd.c,v 1.32 2017/10/12 05:00:23 ryo Exp $");
43 #endif
44 #endif /* not lint */
45 
46 #include <assert.h>
47 #include <err.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <unistd.h>
52 #include <pwd.h>
53 
54 #include "extern.h"
55 
56 #ifdef USE_PAM
57 
58 static void global_usage(const char *);
59 
60 static const struct pw_module_s {
61           const char *argv0;
62           const char *dbname;
63           char compat_opt;
64           void (*pw_usage)(const char *);
65           void (*pw_process)(const char *, int, char **);
66 } pw_modules[] = {
67           /* "files" -- local password database */
68           { NULL, "files", 'l', pwlocal_usage, pwlocal_process },
69 #ifdef YP
70           /* "nis" -- YP/NIS password database */
71           { NULL, "nis", 'y', pwyp_usage, pwyp_process },
72           { "yppasswd", NULL, 0, pwyp_argv0_usage, pwyp_process },
73 #endif
74 #ifdef KERBEROS5
75           /* "krb5" -- Kerberos 5 password database */
76           { NULL, "krb5", 'k', pwkrb5_usage, pwkrb5_process },
77           { "kpasswd", NULL, 0, pwkrb5_argv0_usage, pwkrb5_process },
78 #endif
79           /* default -- use whatever PAM decides */
80           { NULL, NULL, 0, NULL, pwpam_process },
81 
82           { NULL, NULL, 0, NULL, NULL }
83 };
84 
85 static const struct pw_module_s *personality;
86 
87 static void
global_usage(const char * prefix)88 global_usage(const char *prefix)
89 {
90           const struct pw_module_s *pwm;
91 
92           (void) fprintf(stderr, "%s %s [user]\n", prefix, getprogname());
93           for (pwm = pw_modules; pwm->pw_process != NULL; pwm++) {
94                     if (pwm->argv0 == NULL && pwm->pw_usage != NULL)
95                               (*pwm->pw_usage)("      ");
96           }
97 }
98 
99 void
usage(void)100 usage(void)
101 {
102 
103           if (personality != NULL && personality->pw_usage != NULL)
104                     (*personality->pw_usage)("usage:");
105           else
106                     global_usage("usage:");
107           exit(1);
108 }
109 
110 int
main(int argc,char ** argv)111 main(int argc, char **argv)
112 {
113           const struct pw_module_s *pwm;
114           const char *username;
115           int ch, i;
116           char opts[16];
117 
118           /* Build opts string from module compat_opts */
119           i = 0;
120           opts[i++] = 'd';
121           opts[i++] = ':';
122           for (pwm = pw_modules; pwm->pw_process != NULL; pwm++) {
123                     if (pwm->compat_opt != 0)
124                               opts[i++] = pwm->compat_opt;
125           }
126           opts[i++] = '\0';
127 
128           /* First, look for personality based on argv[0]. */
129           for (pwm = pw_modules; pwm->pw_process != NULL; pwm++) {
130                     if (pwm->argv0 != NULL &&
131                         strcmp(pwm->argv0, getprogname()) == 0)
132                               goto got_personality;
133           }
134 
135           /* Try based on compat_opt or -d. */
136           for (ch = 0, pwm = pw_modules; pwm->pw_process != NULL; pwm++) {
137                     if (pwm->argv0 == NULL && pwm->dbname == NULL &&
138                         pwm->compat_opt == 0) {
139                               /*
140                                * We have reached the default personality case.
141                                * Make sure the user didn't provide a bogus
142                                * personality name.
143                                */
144                               if (ch == 'd')
145                                         usage();
146                               break;
147                     }
148 
149                     ch = getopt(argc, argv, opts);
150                     if (ch == '?')
151                               usage();
152 
153                     if (ch == 'd' && pwm->dbname != NULL &&
154                         strcmp(pwm->dbname, optarg) == 0) {
155                               /*
156                                * "passwd -d dbname" matches; this is our
157                                * chosen personality.
158                                */
159                               break;
160                     }
161 
162                     if (pwm->compat_opt != 0 && ch == pwm->compat_opt) {
163                               /*
164                                * Legacy "passwd -l" or similar matches; this
165                                * is our chosen personality.
166                                */
167                               break;
168                     }
169 
170                     /* Reset getopt() and go around again. */
171                     optind = 1;
172                     optreset = 1;
173           }
174 
175  got_personality:
176           personality = pwm;
177 
178           /*
179            * At this point, optind should be either 1 ("passwd"),
180            * 2 ("passwd -l"), or 3 ("passwd -d files").  Consume
181            * these arguments and reset getopt() for the modules to use.
182            */
183           assert(optind >= 1 && optind <= 3);
184           argc -= optind;
185           argv += optind;
186           optind = 0;
187           optreset = 1;
188 
189           username = getlogin();
190           if (username == NULL)
191                     errx(1, "who are you ??");
192 
193           (*personality->pw_process)(username, argc, argv);
194           return 0;
195 }
196 
197 #else /* ! USE_PAM */
198 
199 static struct pw_module_s {
200           const char *argv0;
201           const char *args;
202           const char *usage;
203           int (*pw_init)(const char *);
204           int (*pw_arg)(char, const char *);
205           int (*pw_arg_end)(void);
206           void (*pw_end)(void);
207 
208           int (*pw_chpw)(const char*);
209           int invalid;
210 #define   INIT_INVALID 1
211 #define ARG_INVALID 2
212           int use_class;
213 } pw_modules[] = {
214 #ifdef KERBEROS5
215           { NULL, "5ku:", "[-5] [-k] [-u principal]",
216               krb5_init, krb5_arg, krb5_arg_end, krb5_end, krb5_chpw, 0, 0 },
217           { "kpasswd", "5ku:", "[-5] [-k] [-u principal]",
218               krb5_init, krb5_arg, krb5_arg_end, krb5_end, krb5_chpw, 0, 0 },
219 #endif
220 #ifdef YP
221           { NULL, "y", "[-y]",
222               yp_init, yp_arg, yp_arg_end, yp_end, yp_chpw, 0, 0 },
223           { "yppasswd", "", "[-y]",
224               yp_init, yp_arg, yp_arg_end, yp_end, yp_chpw, 0, 0 },
225 #endif
226           /* local */
227           { NULL, "l", "[-l]",
228               local_init, local_arg, local_arg_end, local_end, local_chpw, 0, 0 },
229 
230           /* terminator */
231           { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
232 };
233 
234 static void __attribute__((__noreturn__))
usage(void)235 usage(void)
236 {
237           int i;
238 
239           fprintf(stderr, "usage:\n");
240           for (i = 0; pw_modules[i].pw_init != NULL; i++)
241                     if (! (pw_modules[i].invalid & INIT_INVALID))
242                               fprintf(stderr, "\t%s %s [user]\n", getprogname(),
243                                   pw_modules[i].usage);
244           exit(1);
245 }
246 
247 int
main(int argc,char ** argv)248 main(int argc, char **argv)
249 {
250           int ch;
251           char *username;
252           char optstring[64];  /* if we ever get more than 64 args, shoot me. */
253           const char *curopt, *oopt;
254           int i, j;
255           int valid;
256           int use_always;
257 
258           /* allow passwd modules to do argv[0] specific processing */
259           use_always = 0;
260           valid = 0;
261           for (i = 0; pw_modules[i].pw_init != NULL; i++) {
262                     pw_modules[i].invalid = 0;
263                     if (pw_modules[i].argv0) {
264                               /*
265                                * If we have a module that matches this progname, be
266                                * sure that no modules but those that match this
267                                * progname can be used.  If we have a module that
268                                * matches against a particular progname, but does NOT
269                                * match this one, don't use that module.
270                                */
271                               if ((strcmp(getprogname(), pw_modules[i].argv0) == 0) &&
272                                   use_always == 0) {
273                                         for (j = 0; j < i; j++) {
274                                                   pw_modules[j].invalid |= INIT_INVALID;
275                                                   (*pw_modules[j].pw_end)();
276                                         }
277                                         use_always = 1;
278                               } else if (use_always == 0)
279                                         pw_modules[i].invalid |= INIT_INVALID;
280                     } else if (use_always)
281                               pw_modules[i].invalid |= INIT_INVALID;
282 
283                     if (pw_modules[i].invalid)
284                               continue;
285 
286                     pw_modules[i].invalid |=
287                         (*pw_modules[i].pw_init)(getprogname()) ?
288                         /* zero on success, non-zero on error */
289                         INIT_INVALID : 0;
290 
291                     if (! pw_modules[i].invalid)
292                               valid = 1;
293           }
294 
295           if (valid == 0)
296                     errx(1, "Can't change password.");
297 
298           /* Build the option string from the individual modules' option
299            * strings.  Note that two modules can share a single option
300            * letter. */
301           optstring[0] = '\0';
302           j = 0;
303           for (i = 0; pw_modules[i].pw_init != NULL; i++) {
304                     if (pw_modules[i].invalid)
305                               continue;
306 
307                     curopt = pw_modules[i].args;
308                     while (*curopt != '\0') {
309                               if ((oopt = strchr(optstring, *curopt)) == NULL) {
310                                         optstring[j++] = *curopt;
311                                         if (curopt[1] == ':') {
312                                                   curopt++;
313                                                   optstring[j++] = *curopt;
314                                         }
315                                         optstring[j] = '\0';
316                               } else if ((oopt[1] == ':' && curopt[1] != ':') ||
317                                   (oopt[1] != ':' && curopt[1] == ':')) {
318                                         errx(1, "NetBSD ERROR!  Different password "
319                                             "modules have two different ideas about "
320                                             "%c argument format.", curopt[0]);
321                               }
322                               curopt++;
323                     }
324           }
325 
326           while ((ch = getopt(argc, argv, optstring)) != -1)
327           {
328                     valid = 0;
329                     for (i = 0; pw_modules[i].pw_init != NULL; i++) {
330                               if (pw_modules[i].invalid)
331                                         continue;
332                               if ((oopt = strchr(pw_modules[i].args, ch)) != NULL) {
333                                         j = (oopt[1] == ':') ?
334                                             ! (*pw_modules[i].pw_arg)(ch, optarg) :
335                                             ! (*pw_modules[i].pw_arg)(ch, NULL);
336                                         if (j != 0)
337                                                   pw_modules[i].invalid |= ARG_INVALID;
338                                         if (pw_modules[i].invalid)
339                                                   (*pw_modules[i].pw_end)();
340                               } else {
341                                         /* arg doesn't match this module */
342                                         pw_modules[i].invalid |= ARG_INVALID;
343                                         (*pw_modules[i].pw_end)();
344                               }
345                               if (! pw_modules[i].invalid)
346                                         valid = 1;
347                     }
348                     if (! valid) {
349                               usage();
350                               exit(1);
351                     }
352           }
353 
354           /* select which module to use to actually change the password. */
355           use_always = 0;
356           valid = 0;
357           for (i = 0; pw_modules[i].pw_init != NULL; i++)
358                     if (! pw_modules[i].invalid) {
359                               pw_modules[i].use_class = (*pw_modules[i].pw_arg_end)();
360                               if (pw_modules[i].use_class != PW_DONT_USE)
361                                         valid = 1;
362                               if (pw_modules[i].use_class == PW_USE_FORCE)
363                                         use_always = 1;
364                     }
365 
366 
367           if (! valid)
368                     /* hang the DJ */
369                     errx(1, "No valid password module specified.");
370 
371           argc -= optind;
372           argv += optind;
373 
374           username = getlogin();
375           if (username == NULL)
376                     errx(1, "who are you ??");
377 
378           switch(argc) {
379           case 0:
380                     break;
381           case 1:
382                     username = argv[0];
383                     break;
384           default:
385                     usage();
386                     exit(1);
387           }
388 
389           /* allow for fallback to other chpw() methods. */
390           for (i = 0; pw_modules[i].pw_init != NULL; i++) {
391                     if (pw_modules[i].invalid)
392                               continue;
393                     if ((use_always && pw_modules[i].use_class == PW_USE_FORCE) ||
394                         (!use_always && pw_modules[i].use_class == PW_USE)) {
395                               valid = (*pw_modules[i].pw_chpw)(username);
396                               (*pw_modules[i].pw_end)();
397                               if (valid >= 0)
398                                         exit(valid);
399                               /* return value < 0 indicates continuation. */
400                     }
401           }
402           exit(1);
403 }
404 
405 #endif /* USE_PAM */
406