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