unstack, sort: cleanup and improvement
[minix.git] / usr.bin / passwd / passwd.c
blob0da7e9bbf4147ecdc923d308507f5c9223ea7704
1 /* $NetBSD: passwd.c,v 1.30 2009/04/17 20:25:08 dyoung Exp $ */
3 /*
4 * Copyright (c) 1988, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
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.
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.
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 */
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.30 2009/04/17 20:25:08 dyoung Exp $");
43 #endif
44 #endif /* not lint */
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>
54 #include "extern.h"
56 #ifdef USE_PAM
58 static void global_usage(const char *);
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 },
82 { NULL, NULL, 0, NULL, NULL }
85 static const struct pw_module_s *personality;
87 static void
88 global_usage(const char *prefix)
90 const struct pw_module_s *pwm;
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)(" ");
99 void
100 usage(void)
103 if (personality != NULL && personality->pw_usage != NULL)
104 (*personality->pw_usage)("usage:");
105 else
106 global_usage("usage:");
107 exit(1);
111 main(int argc, char **argv)
113 const struct pw_module_s *pwm;
114 const char *username;
115 int ch, i;
116 char opts[16];
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;
126 opts[i++] = '\0';
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;
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) {
140 * We have reached the default personality case.
141 * Make sure the user didn't provide a bogus
142 * personality name.
144 if (ch == 'd')
145 usage();
146 break;
149 ch = getopt(argc, argv, opts);
150 if (ch == '?')
151 usage();
153 if (ch == 'd' && pwm->dbname != NULL &&
154 strcmp(pwm->dbname, optarg) == 0) {
156 * "passwd -d dbname" matches; this is our
157 * chosen personality.
159 break;
162 if (pwm->compat_opt != 0 && ch == pwm->compat_opt) {
164 * Legacy "passwd -l" or similar matches; this
165 * is our chosen personality.
167 break;
170 /* Reset getopt() and go around again. */
171 optind = 1;
172 optreset = 1;
175 got_personality:
176 personality = pwm;
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.
183 assert(optind >= 1 && optind <= 3);
184 argc -= optind;
185 argv += optind;
186 optind = 0;
187 optreset = 1;
189 username = getlogin();
190 if (username == NULL)
191 errx(1, "who are you ??");
193 (*personality->pw_process)(username, argc, argv);
194 return 0;
197 #else /* ! USE_PAM */
199 static struct pw_module_s {
200 const char *argv0;
201 const char *args;
202 const char *usage;
203 int (*pw_init) __P((const char *));
204 int (*pw_arg) __P((char, const char *));
205 int (*pw_arg_end) __P((void));
206 void (*pw_end) __P((void));
208 int (*pw_chpw) __P((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 },
230 /* terminator */
231 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
234 static void
235 usage(void)
237 int i;
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);
248 main(int argc, char **argv)
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;
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) {
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.
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)();
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;
283 if (pw_modules[i].invalid)
284 continue;
286 pw_modules[i].invalid |=
287 (*pw_modules[i].pw_init)(getprogname()) ?
288 /* zero on success, non-zero on error */
289 INIT_INVALID : 0;
291 if (! pw_modules[i].invalid)
292 valid = 1;
295 if (valid == 0)
296 errx(1, "Can't change password.");
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;
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;
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]);
322 curopt++;
326 while ((ch = getopt(argc, argv, optstring)) != -1)
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)();
345 if (! pw_modules[i].invalid)
346 valid = 1;
348 if (! valid) {
349 usage();
350 exit(1);
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;
367 if (! valid)
368 /* hang the DJ */
369 errx(1, "No valid password module specified.");
371 argc -= optind;
372 argv += optind;
374 username = getlogin();
375 if (username == NULL)
376 errx(1, "who are you ??");
378 switch(argc) {
379 case 0:
380 break;
381 case 1:
382 username = argv[0];
383 break;
384 default:
385 usage();
386 exit(1);
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. */
402 exit(1);
405 #endif /* USE_PAM */