1 /* $NetBSD: passwd.c,v 1.30 2009/04/17 20:25:08 dyoung 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.30 2009/04/17 20:25:08 dyoung 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 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 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 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) __P((const char *)); 204 int (*pw_arg) __P((char, const char *)); 205 int (*pw_arg_end) __P((void)); 206 void (*pw_end) __P((void)); 207 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 }, 229 230 /* terminator */ 231 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } 232 }; 233 234 #if defined(__minix) 235 static __dead void 236 #else 237 static void 238 #endif /* defined(__minix) */ 239 usage(void) 240 { 241 int i; 242 243 fprintf(stderr, "usage:\n"); 244 for (i = 0; pw_modules[i].pw_init != NULL; i++) 245 if (! (pw_modules[i].invalid & INIT_INVALID)) 246 fprintf(stderr, "\t%s %s [user]\n", getprogname(), 247 pw_modules[i].usage); 248 exit(1); 249 } 250 251 int 252 main(int argc, char **argv) 253 { 254 int ch; 255 char *username; 256 char optstring[64]; /* if we ever get more than 64 args, shoot me. */ 257 const char *curopt, *oopt; 258 int i, j; 259 int valid; 260 int use_always; 261 262 /* allow passwd modules to do argv[0] specific processing */ 263 use_always = 0; 264 valid = 0; 265 for (i = 0; pw_modules[i].pw_init != NULL; i++) { 266 pw_modules[i].invalid = 0; 267 if (pw_modules[i].argv0) { 268 /* 269 * If we have a module that matches this progname, be 270 * sure that no modules but those that match this 271 * progname can be used. If we have a module that 272 * matches against a particular progname, but does NOT 273 * match this one, don't use that module. 274 */ 275 if ((strcmp(getprogname(), pw_modules[i].argv0) == 0) && 276 use_always == 0) { 277 for (j = 0; j < i; j++) { 278 pw_modules[j].invalid |= INIT_INVALID; 279 (*pw_modules[j].pw_end)(); 280 } 281 use_always = 1; 282 } else if (use_always == 0) 283 pw_modules[i].invalid |= INIT_INVALID; 284 } else if (use_always) 285 pw_modules[i].invalid |= INIT_INVALID; 286 287 if (pw_modules[i].invalid) 288 continue; 289 290 pw_modules[i].invalid |= 291 (*pw_modules[i].pw_init)(getprogname()) ? 292 /* zero on success, non-zero on error */ 293 INIT_INVALID : 0; 294 295 if (! pw_modules[i].invalid) 296 valid = 1; 297 } 298 299 if (valid == 0) 300 errx(1, "Can't change password."); 301 302 /* Build the option string from the individual modules' option 303 * strings. Note that two modules can share a single option 304 * letter. */ 305 optstring[0] = '\0'; 306 j = 0; 307 for (i = 0; pw_modules[i].pw_init != NULL; i++) { 308 if (pw_modules[i].invalid) 309 continue; 310 311 curopt = pw_modules[i].args; 312 while (*curopt != '\0') { 313 if ((oopt = strchr(optstring, *curopt)) == NULL) { 314 optstring[j++] = *curopt; 315 if (curopt[1] == ':') { 316 curopt++; 317 optstring[j++] = *curopt; 318 } 319 optstring[j] = '\0'; 320 } else if ((oopt[1] == ':' && curopt[1] != ':') || 321 (oopt[1] != ':' && curopt[1] == ':')) { 322 errx(1, "NetBSD ERROR! Different password " 323 "modules have two different ideas about " 324 "%c argument format.", curopt[0]); 325 } 326 curopt++; 327 } 328 } 329 330 while ((ch = getopt(argc, argv, optstring)) != -1) 331 { 332 valid = 0; 333 for (i = 0; pw_modules[i].pw_init != NULL; i++) { 334 if (pw_modules[i].invalid) 335 continue; 336 if ((oopt = strchr(pw_modules[i].args, ch)) != NULL) { 337 j = (oopt[1] == ':') ? 338 ! (*pw_modules[i].pw_arg)(ch, optarg) : 339 ! (*pw_modules[i].pw_arg)(ch, NULL); 340 if (j != 0) 341 pw_modules[i].invalid |= ARG_INVALID; 342 if (pw_modules[i].invalid) 343 (*pw_modules[i].pw_end)(); 344 } else { 345 /* arg doesn't match this module */ 346 pw_modules[i].invalid |= ARG_INVALID; 347 (*pw_modules[i].pw_end)(); 348 } 349 if (! pw_modules[i].invalid) 350 valid = 1; 351 } 352 if (! valid) { 353 usage(); 354 exit(1); 355 } 356 } 357 358 /* select which module to use to actually change the password. */ 359 use_always = 0; 360 valid = 0; 361 for (i = 0; pw_modules[i].pw_init != NULL; i++) 362 if (! pw_modules[i].invalid) { 363 pw_modules[i].use_class = (*pw_modules[i].pw_arg_end)(); 364 if (pw_modules[i].use_class != PW_DONT_USE) 365 valid = 1; 366 if (pw_modules[i].use_class == PW_USE_FORCE) 367 use_always = 1; 368 } 369 370 371 if (! valid) 372 /* hang the DJ */ 373 errx(1, "No valid password module specified."); 374 375 argc -= optind; 376 argv += optind; 377 378 username = getlogin(); 379 if (username == NULL) 380 errx(1, "who are you ??"); 381 382 switch(argc) { 383 case 0: 384 break; 385 case 1: 386 username = argv[0]; 387 break; 388 default: 389 usage(); 390 exit(1); 391 } 392 393 /* allow for fallback to other chpw() methods. */ 394 for (i = 0; pw_modules[i].pw_init != NULL; i++) { 395 if (pw_modules[i].invalid) 396 continue; 397 if ((use_always && pw_modules[i].use_class == PW_USE_FORCE) || 398 (!use_always && pw_modules[i].use_class == PW_USE)) { 399 valid = (*pw_modules[i].pw_chpw)(username); 400 (*pw_modules[i].pw_end)(); 401 if (valid >= 0) 402 exit(valid); 403 /* return value < 0 indicates continuation. */ 404 } 405 } 406 exit(1); 407 } 408 409 #endif /* USE_PAM */ 410