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