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