1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* pwconv.c */ 31 /* Conversion aid to copy appropriate fields from the */ 32 /* password file to the shadow file. */ 33 34 #include <pwd.h> 35 #include <fcntl.h> 36 #include <stdio.h> 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <time.h> 40 #include <shadow.h> 41 #include <grp.h> 42 #include <signal.h> 43 #include <errno.h> 44 #include <unistd.h> 45 #include <stdlib.h> 46 #include <locale.h> 47 #include <string.h> 48 49 #define PRIVILEGED 0 /* privileged id */ 50 51 /* exit code */ 52 #define SUCCESS 0 /* succeeded */ 53 #define NOPERM 1 /* No permission */ 54 #define BADSYN 2 /* Incorrect syntax */ 55 #define FMERR 3 /* File manipulation error */ 56 #define FATAL 4 /* Old file can not be recover */ 57 #define FBUSY 5 /* Lock file busy */ 58 #define BADSHW 6 /* Bad entry in shadow file */ 59 60 #define DELPTMP() (void) unlink(PASSTEMP) 61 #define DELSHWTMP() (void) unlink(SHADTEMP) 62 63 char pwdflr[] = "x"; /* password filler */ 64 char *prognamp; 65 void f_err(void), f_miss(void), f_bdshw(void); 66 67 /* 68 * getspnan routine that ONLY looks at the local shadow file 69 */ 70 struct spwd * 71 local_getspnam(char *name) 72 { 73 FILE *shadf; 74 struct spwd *sp; 75 76 77 if ((shadf = fopen("/etc/shadow", "r")) == NULL) 78 return (NULL); 79 80 while ((sp = fgetspent(shadf)) != NULL) { 81 if (strcmp(sp->sp_namp, name) == 0) 82 break; 83 } 84 85 (void) fclose(shadf); 86 87 return (sp); 88 } 89 90 int 91 main(int argc, char **argv) 92 { 93 extern int errno; 94 void no_recover(void), no_convert(void); 95 struct passwd *pwdp; 96 struct spwd *sp, sp_pwd; /* default entry */ 97 struct stat buf; 98 FILE *tp_fp, *tsp_fp; 99 time_t when, minweeks, maxweeks; 100 int file_exist = 1; 101 int end_of_file = 0; 102 mode_t mode; 103 mode_t pwd_mode; 104 int pwerr = 0; 105 ushort_t i; 106 gid_t pwd_gid, sp_gid; 107 uid_t pwd_uid, sp_uid; 108 FILE *pwf; 109 int black_magic = 0; 110 int count; 111 112 (void) setlocale(LC_ALL, ""); 113 114 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 115 #define TEXT_DOMAIN "SYS_TEST" 116 #endif 117 (void) textdomain(TEXT_DOMAIN); 118 119 prognamp = argv[0]; 120 /* only PRIVILEGED can execute this command */ 121 if (getuid() != PRIVILEGED) { 122 (void) fprintf(stderr, gettext("%s: Permission denied.\n"), 123 prognamp); 124 exit(NOPERM); 125 } 126 127 /* No argument can be passed to the command */ 128 if (argc > 1) { 129 (void) fprintf(stderr, 130 gettext("%s: Invalid command syntax.\n"), prognamp); 131 (void) fprintf(stderr, gettext("Usage: pwconv\n")); 132 exit(BADSYN); 133 } 134 135 /* lock file so that only one process can read or write at a time */ 136 if (lckpwdf() < 0) { 137 (void) fprintf(stderr, 138 gettext("%s: Password file(s) busy. Try again later.\n"), 139 prognamp); 140 exit(FBUSY); 141 } 142 143 /* All signals will be ignored during the execution of pwconv */ 144 for (i = 1; i < NSIG; i++) 145 (void) sigset(i, SIG_IGN); 146 147 /* reset errno to avoid side effects of a failed */ 148 /* sigset (e.g., SIGKILL) */ 149 errno = 0; 150 151 /* check the file status of the password file */ 152 /* get the gid of the password file */ 153 if (stat(PASSWD, &buf) < 0) { 154 (void) f_miss(); 155 exit(FATAL); 156 } 157 pwd_gid = buf.st_gid; 158 pwd_uid = buf.st_uid; 159 pwd_mode = buf.st_mode; 160 161 /* mode for the password file should be read-only or less */ 162 (void) umask(S_IAMB & ~(buf.st_mode & (S_IRUSR|S_IRGRP|S_IROTH))); 163 164 /* open temporary password file */ 165 if ((tp_fp = fopen(PASSTEMP, "w")) == NULL) { 166 (void) f_err(); 167 exit(FMERR); 168 } 169 170 if (chown(PASSTEMP, pwd_uid, pwd_gid) < 0) { 171 DELPTMP(); 172 (void) f_err(); 173 exit(FMERR); 174 } 175 /* default mode mask of the shadow file */ 176 mode = S_IAMB & ~(S_IRUSR); 177 178 /* check the existence of shadow file */ 179 /* if the shadow file exists, get mode mask and group id of the file */ 180 /* if file does not exist, the default group name will be the group */ 181 /* name of the password file. */ 182 183 if (access(SHADOW, F_OK) == 0) { 184 if (stat(SHADOW, &buf) == 0) { 185 mode = S_IAMB & ~(buf.st_mode & S_IRUSR); 186 sp_gid = buf.st_gid; 187 sp_uid = buf.st_uid; 188 } else { 189 DELPTMP(); 190 (void) f_err(); 191 exit(FMERR); 192 } 193 } else { 194 sp_gid = pwd_gid; 195 sp_uid = pwd_uid; 196 file_exist = 0; 197 } 198 /* 199 * get the mode of shadow password file -- mode of the file should 200 * be read-only for user or less. 201 */ 202 (void) umask(mode); 203 204 /* open temporary shadow file */ 205 if ((tsp_fp = fopen(SHADTEMP, "w")) == NULL) { 206 DELPTMP(); 207 (void) f_err(); 208 exit(FMERR); 209 } 210 211 /* change the group of the temporary shadow password file */ 212 if (chown(SHADTEMP, sp_uid, sp_gid) < 0) { 213 (void) no_convert(); 214 exit(FMERR); 215 } 216 217 /* Reads the password file. */ 218 /* If the shadow password file not exists, or */ 219 /* if an entry doesn't have a corresponding entry in */ 220 /* the shadow file, entries/entry will be created. */ 221 222 if ((pwf = fopen("/etc/passwd", "r")) == NULL) { 223 no_recover(); 224 exit(FATAL); 225 } 226 227 count = 0; 228 while (!end_of_file) { 229 count++; 230 if ((pwdp = fgetpwent(pwf)) != NULL) { 231 if (!file_exist || 232 (sp = local_getspnam(pwdp->pw_name)) == NULL) { 233 if (errno == EINVAL) { 234 /* Bad entry in shadow exit */ 235 DELSHWTMP(); 236 DELPTMP(); 237 (void) f_bdshw(); 238 exit(BADSHW); 239 } 240 sp = &sp_pwd; 241 sp->sp_namp = pwdp->pw_name; 242 if (!pwdp->pw_passwd || 243 (pwdp->pw_passwd && 244 *pwdp->pw_passwd == '\0')) { 245 (void) fprintf(stderr, gettext( 246 "%s: WARNING user %s has no " 247 "password\n"), 248 prognamp, sp->sp_namp); 249 } 250 /* 251 * copy the password field in the password 252 * file to the shadow file. 253 * replace the password field with an 'x'. 254 */ 255 sp->sp_pwdp = pwdp->pw_passwd; 256 pwdp->pw_passwd = pwdflr; 257 /* 258 * if aging, split the aging info 259 * into age, max and min 260 * convert aging info from weeks to days 261 */ 262 if (pwdp->pw_age && *pwdp->pw_age != 0) { 263 when = (long)a64l(pwdp->pw_age); 264 maxweeks = when & 077; 265 minweeks = (when >> 6) & 077; 266 when >>= 12; 267 sp->sp_lstchg = when * 7; 268 sp->sp_min = minweeks * 7; 269 sp->sp_max = maxweeks * 7; 270 sp->sp_warn = -1; 271 sp->sp_inact = -1; 272 sp->sp_expire = -1; 273 sp->sp_flag = 0; 274 pwdp->pw_age = ""; /* do we care? */ 275 } else { 276 /* 277 * if !aging, last_changed will be the day the 278 * conversion is done, min and max fields will 279 * be null - use timezone to get local time 280 */ 281 sp->sp_lstchg = DAY_NOW; 282 sp->sp_min = -1; 283 sp->sp_max = -1; 284 sp->sp_warn = -1; 285 sp->sp_inact = -1; 286 sp->sp_expire = -1; 287 sp->sp_flag = 0; 288 } 289 } else { 290 /* 291 * if the passwd field has a string other than 292 * 'x', the entry will be written into the 293 * shadow file and the character 'x' is 294 * re-written as the passwd if !aging, 295 * last_changed as above 296 */ 297 298 /* 299 * with NIS, only warn about password missing 300 * if entry is not a NIS-lookup entry 301 * ("+" or "-") black_magic from getpwnam_r.c 302 */ 303 black_magic = (*pwdp->pw_name == '+' || 304 *pwdp->pw_name == '-'); 305 /* 306 * moan about absence of non "+/-" passwd 307 * we could do more, but what? 308 */ 309 if ((!pwdp->pw_passwd || 310 (pwdp->pw_passwd && 311 *pwdp->pw_passwd == '\0')) && 312 !black_magic) { 313 (void) fprintf(stderr, gettext( 314 "%s: WARNING user %s has no " 315 "password\n"), 316 prognamp, sp->sp_namp); 317 } 318 if (pwdp->pw_passwd && *pwdp->pw_passwd) { 319 if (strcmp(pwdp->pw_passwd, pwdflr)) { 320 sp->sp_pwdp = pwdp->pw_passwd; 321 pwdp->pw_passwd = pwdflr; 322 if (!pwdp->pw_age || 323 (pwdp->pw_age && 324 *pwdp->pw_age == 0)) { 325 sp->sp_lstchg = DAY_NOW; 326 sp->sp_min = -1; 327 sp->sp_max = -1; 328 sp->sp_warn = -1; 329 sp->sp_inact = -1; 330 sp->sp_expire = -1; 331 sp->sp_flag = 0; 332 } 333 } 334 } else { 335 /* 336 * black_magic needs a non-null passwd 337 * and pwdflr seem appropriate here 338 * clear garbage if any 339 */ 340 sp->sp_pwdp = ""; 341 pwdp->pw_passwd = pwdflr; 342 sp->sp_lstchg = sp->sp_min = 343 sp->sp_max = -1; 344 sp->sp_warn = sp->sp_inact = 345 sp->sp_expire = -1; 346 sp->sp_flag = 0; 347 } 348 /* 349 * if aging, split the aging info 350 * into age, max and min 351 * convert aging info from weeks to days 352 */ 353 if (pwdp->pw_age && *pwdp->pw_age != '\0') { 354 when = (long)a64l(pwdp->pw_age); 355 maxweeks = when & 077; 356 minweeks = (when >> 6) & 077; 357 when >>= 12; 358 sp->sp_lstchg = when * 7; 359 sp->sp_min = minweeks * 7; 360 sp->sp_max = maxweeks * 7; 361 sp->sp_warn = -1; 362 sp->sp_inact = -1; 363 sp->sp_expire = -1; 364 sp->sp_flag = 0; 365 pwdp->pw_age = ""; /* do we care? */ 366 } 367 } 368 369 /* write an entry to temporary password file */ 370 if ((putpwent(pwdp, tp_fp)) != 0) { 371 (void) no_convert(); 372 exit(FMERR); 373 } 374 375 /* write an entry to temporary shadow password file */ 376 if (putspent(sp, tsp_fp) != 0) { 377 (void) no_convert(); 378 exit(FMERR); 379 } 380 } else { 381 if (feof(pwf)) { 382 end_of_file = 1; 383 } else { 384 errno = 0; 385 pwerr = 1; 386 (void) fprintf(stderr, 387 gettext("%s: ERROR: bad entry or blank " 388 "line at line %d in /etc/passwd\n"), 389 prognamp, count); 390 } 391 } 392 } /* end of while */ 393 394 (void) fclose(pwf); 395 (void) fclose(tsp_fp); 396 (void) fclose(tp_fp); 397 if (pwerr) { 398 (void) no_convert(); 399 exit(FMERR); 400 } 401 402 /* delete old password file if it exists */ 403 if (unlink(OPASSWD) && (access(OPASSWD, F_OK) == 0)) { 404 (void) no_convert(); 405 exit(FMERR); 406 } 407 408 /* rename the password file to old password file */ 409 if (rename(PASSWD, OPASSWD) == -1) { 410 (void) no_convert(); 411 exit(FMERR); 412 } 413 414 /* rename temporary password file to password file */ 415 if (rename(PASSTEMP, PASSWD) == -1) { 416 /* link old password file to password file */ 417 if (link(OPASSWD, PASSWD) < 0) { 418 (void) no_recover(); 419 exit(FATAL); 420 } 421 (void) no_convert(); 422 exit(FMERR); 423 } 424 425 /* delete old shadow password file if it exists */ 426 if (unlink(OSHADOW) && (access(OSHADOW, R_OK) == 0)) { 427 /* link old password file to password file */ 428 if (unlink(PASSWD) || link(OPASSWD, PASSWD)) { 429 (void) no_recover(); 430 exit(FATAL); 431 } 432 (void) no_convert(); 433 exit(FMERR); 434 } 435 436 /* link shadow password file to old shadow password file */ 437 if (file_exist && rename(SHADOW, OSHADOW)) { 438 /* link old password file to password file */ 439 if (unlink(PASSWD) || link(OPASSWD, PASSWD)) { 440 (void) no_recover(); 441 exit(FATAL); 442 } 443 (void) no_convert(); 444 exit(FMERR); 445 } 446 447 448 /* link temporary shadow password file to shadow password file */ 449 if (rename(SHADTEMP, SHADOW) == -1) { 450 /* link old shadow password file to shadow password file */ 451 if (file_exist && (link(OSHADOW, SHADOW))) { 452 (void) no_recover(); 453 exit(FATAL); 454 } 455 if (unlink(PASSWD) || link(OPASSWD, PASSWD)) { 456 (void) no_recover(); 457 exit(FATAL); 458 } 459 (void) no_convert(); 460 exit(FMERR); 461 } 462 463 /* Make new mode same as old */ 464 (void) chmod(PASSWD, pwd_mode); 465 466 /* Change old password file to read only by owner */ 467 /* If chmod fails, delete the old password file so that */ 468 /* the password fields can not be read by others */ 469 if (chmod(OPASSWD, S_IRUSR) < 0) 470 (void) unlink(OPASSWD); 471 472 (void) ulckpwdf(); 473 return (0); 474 } 475 476 void 477 no_recover(void) 478 { 479 DELPTMP(); 480 DELSHWTMP(); 481 (void) f_miss(); 482 } 483 484 void 485 no_convert(void) 486 { 487 DELPTMP(); 488 DELSHWTMP(); 489 (void) f_err(); 490 } 491 492 void 493 f_err(void) 494 { 495 (void) fprintf(stderr, 496 gettext("%s: Unexpected failure. Conversion not done.\n"), 497 prognamp); 498 (void) ulckpwdf(); 499 } 500 501 void 502 f_miss(void) 503 { 504 (void) fprintf(stderr, 505 gettext("%s: Unexpected failure. Password file(s) missing.\n"), 506 prognamp); 507 (void) ulckpwdf(); 508 } 509 510 void 511 f_bdshw(void) 512 { 513 (void) fprintf(stderr, 514 gettext("%s: Bad entry in /etc/shadow. Conversion not done.\n"), 515 prognamp); 516 (void) ulckpwdf(); 517 } 518