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 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)(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__)) 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 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