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.
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.
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]
23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
30 #pragma ident "%Z%%M% %I% %E% SMI"
33 /* Conversion aid to copy appropriate fields from the */
34 /* password file to the shadow file. */
39 #include <sys/types.h>
51 #define PRIVILEGED 0 /* priviledged id */
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 */
62 #define DELPTMP() (void) unlink(PASSTEMP)
63 #define DELSHWTMP() (void) unlink(SHADTEMP)
65 char pwdflr
[] = "x"; /* password filler */
67 void f_err(void), f_miss(void), f_bdshw(void);
70 * getspnan routine that ONLY looks at the local shadow file
73 local_getspnam(char *name
)
79 if ((shadf
= fopen("/etc/shadow", "r")) == NULL
)
82 while ((sp
= fgetspent(shadf
)) != NULL
) {
83 if (strcmp(sp
->sp_namp
, name
) == 0)
93 main(int argc
, char **argv
)
96 void no_recover(void), no_convert(void);
98 struct spwd
*sp
, sp_pwd
; /* default entry */
100 FILE *tp_fp
, *tsp_fp
;
101 time_t when
, minweeks
, maxweeks
;
108 gid_t pwd_gid
, sp_gid
;
109 uid_t pwd_uid
, sp_uid
;
114 (void) setlocale(LC_ALL
, "");
116 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
117 #define TEXT_DOMAIN "SYS_TEST"
119 (void) textdomain(TEXT_DOMAIN
);
122 /* only PRIVILEGED can execute this command */
123 if (getuid() != PRIVILEGED
) {
124 (void) fprintf(stderr
, gettext("%s: Permission denied.\n"),
129 /* No argument can be passed to the command */
131 (void) fprintf(stderr
,
132 gettext("%s: Invalid command syntax.\n"),
134 (void) fprintf(stderr
, gettext("Usage: pwconv\n"));
138 /* lock file so that only one process can read or write at a time */
140 (void) fprintf(stderr
,
141 gettext("%s: Password file(s) busy. Try again later.\n"),
146 /* All signals will be ignored during the execution of pwconv */
147 for (i
= 1; i
< NSIG
; i
++)
148 (void) sigset(i
, SIG_IGN
);
150 /* reset errno to avoid side effects of a failed */
151 /* sigset (e.g., SIGKILL) */
154 /* check the file status of the password file */
155 /* get the gid of the password file */
156 if (stat(PASSWD
, &buf
) < 0) {
160 pwd_gid
= buf
.st_gid
;
161 pwd_uid
= buf
.st_uid
;
162 pwd_mode
= buf
.st_mode
;
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
)));
167 /* open temporary password file */
168 if ((tp_fp
= fopen(PASSTEMP
, "w")) == NULL
) {
173 if (chown(PASSTEMP
, pwd_uid
, pwd_gid
) < 0) {
178 /* default mode mask of the shadow file */
179 mode
= S_IAMB
& ~(S_IRUSR
);
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. */
186 if (access(SHADOW
, F_OK
) == 0) {
187 if (stat(SHADOW
, &buf
) == 0) {
188 mode
= S_IAMB
& ~(buf
.st_mode
& S_IRUSR
);
202 * get the mode of shadow password file -- mode of the file should
203 * be read-only for user or less.
207 /* open temporary shadow file */
208 if ((tsp_fp
= fopen(SHADTEMP
, "w")) == NULL
) {
214 /* change the group of the temporary shadow password file */
215 if (chown(SHADTEMP
, sp_uid
, sp_gid
) < 0) {
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. */
225 if ((pwf
= fopen("/etc/passwd", "r")) == NULL
) {
231 while (!end_of_file
) {
233 if ((pwdp
= fgetpwent(pwf
)) != NULL
) {
235 (sp
= local_getspnam(pwdp
->pw_name
)) == NULL
) {
236 if (errno
== EINVAL
) {
237 /* Bad entry in shadow exit */
244 sp
->sp_namp
= pwdp
->pw_name
;
245 if (!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
);
253 * copy the password field in the password
254 * file to the shadow file.
255 * replace the password field with an 'x'.
257 sp
->sp_pwdp
= pwdp
->pw_passwd
;
258 pwdp
->pw_passwd
= pwdflr
;
260 * if aging, split the aging info
261 * into age, max and min
262 * convert aging info from weeks to days
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;
269 sp
->sp_lstchg
= when
* 7;
270 sp
->sp_min
= minweeks
* 7;
271 sp
->sp_max
= maxweeks
* 7;
276 pwdp
->pw_age
= ""; /* do we care? */
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
283 sp
->sp_lstchg
= DAY_NOW
;
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
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
304 black_magic
= (*pwdp
->pw_name
== '+' ||
305 *pwdp
->pw_name
== '-');
307 * moan about absence of non "+/-" passwd
308 * we could do more, but what?
310 if ((!pwdp
->pw_passwd
||
311 (pwdp
->pw_passwd
&& *pwdp
->pw_passwd
== NULL
)) &&
313 (void) fprintf(stderr
,
314 gettext("%s: WARNING user %s has no password\n"),
315 prognamp
, sp
->sp_namp
);
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
;
323 *pwdp
->pw_age
== NULL
)) {
324 sp
->sp_lstchg
= DAY_NOW
;
335 * black_magic needs a non-null passwd
336 * and pwdflr seem appropriate here
337 * clear garbage if any
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;
346 * if aging, split the aging info
347 * into age, max and min
348 * convert aging info from weeks to days
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;
355 sp
->sp_lstchg
= when
* 7;
356 sp
->sp_min
= minweeks
* 7;
357 sp
->sp_max
= maxweeks
* 7;
362 pwdp
->pw_age
= ""; /* do we care? */
366 /* write an entry to temporary password file */
367 if ((putpwent(pwdp
, tp_fp
)) != 0) {
372 /* write an entry to temporary shadow password file */
373 if (putspent(sp
, tsp_fp
) != 0) {
383 (void) fprintf(stderr
,
384 gettext("%s: ERROR: bad entry or blank "
385 "line at line %d in /etc/passwd\n"),
392 (void) fclose(tsp_fp
);
393 (void) fclose(tp_fp
);
399 /* delete old password file if it exists */
400 if (unlink(OPASSWD
) && (access(OPASSWD
, F_OK
) == 0)) {
405 /* rename the password file to old password file */
406 if (rename(PASSWD
, OPASSWD
) == -1) {
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) {
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
)) {
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
)) {
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
))) {
452 if (unlink(PASSWD
) || link(OPASSWD
, PASSWD
)) {
460 /* Make new mode same as old */
461 (void) chmod(PASSWD
, pwd_mode
);
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
);
492 (void) fprintf(stderr
,
493 gettext("%s: Unexpected failure. Conversion not done.\n"),
501 (void) fprintf(stderr
,
502 gettext("%s: Unexpected failure. Password file(s) missing.\n"),
510 (void) fprintf(stderr
,
511 gettext("%s: Bad entry in /etc/shadow. Conversion not done.\n"),