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