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