Initial commit for Tizen
[profile/extras/shadow-utils.git] / src / pwconv.c
1 /*
2  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
3  * Copyright (c) 2002 - 2006, Tomasz Kłoczko
4  * Copyright (c) 2009       , Nicolas François
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. The name of the copyright holders or contributors may not be used to
16  *    endorse or promote products derived from this software without
17  *    specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
23  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 /*
33  * pwconv - create or update /etc/shadow with information from
34  * /etc/passwd.
35  *
36  * It is more like SysV pwconv, slightly different from the original Shadow
37  * pwconv. Depends on "x" as password in /etc/passwd which means that the
38  * password has already been moved to /etc/shadow. There is no need to move
39  * /etc/npasswd to /etc/passwd, password files are updated using library
40  * routines with proper locking.
41  *
42  * Can be used to update /etc/shadow after adding/deleting users by editing
43  * /etc/passwd. There is no man page yet, but this program should be close
44  * to pwconv(1M) on Solaris 2.x.
45  *
46  * Warning: make sure that all users have "x" as the password in /etc/passwd
47  * before running this program for the first time on a system which already
48  * has shadow passwords. Anything else (like "*" from old versions of the
49  * shadow suite) will replace the user's encrypted password in /etc/shadow.
50  *
51  * Doesn't currently support pw_age information in /etc/passwd, and doesn't
52  * support DBM files. Add it if you need it...
53  *
54  */
55
56 #include <config.h>
57
58 #ident "$Id: pwconv.c 2851 2009-04-30 21:39:38Z nekral-guest $"
59
60 #include <errno.h>
61 #include <fcntl.h>
62 #include <pwd.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <time.h>
67 #include <unistd.h>
68 #include "defines.h"
69 #include "getdef.h"
70 #include "prototypes.h"
71 #include "pwio.h"
72 #include "shadowio.h"
73 #include "nscd.h"
74
75 /*
76  * exit status values
77  */
78 /*@-exitarg@*/
79 #define E_SUCCESS       0       /* success */
80 #define E_NOPERM        1       /* permission denied */
81 #define E_USAGE         2       /* invalid command syntax */
82 #define E_FAILURE       3       /* unexpected failure, nothing done */
83 #define E_MISSING       4       /* unexpected failure, passwd file missing */
84 #define E_PWDBUSY       5       /* passwd file(s) busy */
85 #define E_BADENTRY      6       /* bad shadow entry */
86 /*
87  * Global variables
88  */
89 char *Prog;
90
91 static bool spw_locked = false;
92 static bool pw_locked = false;
93
94 /* local function prototypes */
95 static void fail_exit (int status);
96
97 static void fail_exit (int status)
98 {
99         if (pw_locked) {
100                 if (pw_unlock () == 0) {
101                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
102                         SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
103                         /* continue */
104                 }
105         }
106
107         if (spw_locked) {
108                 if (spw_unlock () == 0) {
109                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
110                         SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
111                         /* continue */
112                 }
113         }
114
115         exit (status);
116 }
117
118 int main (int argc, char **argv)
119 {
120         const struct passwd *pw;
121         struct passwd pwent;
122         const struct spwd *sp;
123         struct spwd spent;
124
125         if (1 != argc) {
126                 (void) fputs (_("Usage: pwconv\n"), stderr);
127         }
128         Prog = Basename (argv[0]);
129
130         (void) setlocale (LC_ALL, "");
131         (void) bindtextdomain (PACKAGE, LOCALEDIR);
132         (void) textdomain (PACKAGE);
133
134         OPENLOG ("pwconv");
135
136         if (pw_lock () == 0) {
137                 fprintf (stderr,
138                          _("%s: cannot lock %s; try again later.\n"),
139                          Prog, pw_dbname ());
140                 fail_exit (E_PWDBUSY);
141         }
142         pw_locked = true;
143         if (pw_open (O_RDWR) == 0) {
144                 fprintf (stderr,
145                          _("%s: cannot open %s\n"), Prog, pw_dbname ());
146                 fail_exit (E_MISSING);
147         }
148
149         if (spw_lock () == 0) {
150                 fprintf (stderr,
151                          _("%s: cannot lock %s; try again later.\n"),
152                          Prog, spw_dbname ());
153                 fail_exit (E_PWDBUSY);
154         }
155         spw_locked = true;
156         if (spw_open (O_CREAT | O_RDWR) == 0) {
157                 fprintf (stderr,
158                          _("%s: cannot open %s\n"), Prog, spw_dbname ());
159                 fail_exit (E_FAILURE);
160         }
161
162         /*
163          * Remove /etc/shadow entries for users not in /etc/passwd.
164          */
165         spw_rewind ();
166         while ((sp = spw_next ()) != NULL) {
167                 if (pw_locate (sp->sp_namp) != NULL) {
168                         continue;
169                 }
170
171                 if (spw_remove (sp->sp_namp) == 0) {
172                         /*
173                          * This shouldn't happen (the entry exists) but...
174                          */
175                         fprintf (stderr,
176                                  _("%s: cannot remove entry '%s' from %s\n"),
177                                  Prog, sp->sp_namp, spw_dbname ());
178                         fail_exit (E_FAILURE);
179                 }
180         }
181
182         /*
183          * Update shadow entries which don't have "x" as pw_passwd. Add any
184          * missing shadow entries.
185          */
186         pw_rewind ();
187         while ((pw = pw_next ()) != NULL) {
188                 sp = spw_locate (pw->pw_name);
189                 if (NULL != sp) {
190                         /* do we need to update this entry? */
191                         if (strcmp (pw->pw_passwd, SHADOW_PASSWD_STRING) == 0) {
192                                 continue;
193                         }
194                         /* update existing shadow entry */
195                         spent = *sp;
196                 } else {
197                         /* add new shadow entry */
198                         memset (&spent, 0, sizeof spent);
199                         spent.sp_namp   = pw->pw_name;
200                         spent.sp_min    = getdef_num ("PASS_MIN_DAYS", -1);
201                         spent.sp_max    = getdef_num ("PASS_MAX_DAYS", -1);
202                         spent.sp_warn   = getdef_num ("PASS_WARN_AGE", -1);
203                         spent.sp_inact  = -1;
204                         spent.sp_expire = -1;
205                         spent.sp_flag   = SHADOW_SP_FLAG_UNSET;
206                 }
207                 spent.sp_pwdp = pw->pw_passwd;
208                 spent.sp_lstchg = (long) time ((time_t *) 0) / SCALE;
209                 if (0 == spent.sp_lstchg) {
210                         /* Better disable aging than requiring a password
211                          * change */
212                         spent.sp_lstchg = -1;
213                 }
214                 if (spw_update (&spent) == 0) {
215                         fprintf (stderr,
216                                  _("%s: failed to prepare the new %s entry '%s'\n"),
217                                  Prog, spw_dbname (), spent.sp_namp);
218                         fail_exit (E_FAILURE);
219                 }
220
221                 /* remove password from /etc/passwd */
222                 pwent = *pw;
223                 pwent.pw_passwd = SHADOW_PASSWD_STRING; /* XXX warning: const */
224                 if (pw_update (&pwent) == 0) {
225                         fprintf (stderr,
226                                  _("%s: failed to prepare the new %s entry '%s'\n"),
227                                  Prog, pw_dbname (), pwent.pw_name);
228                         fail_exit (E_FAILURE);
229                 }
230         }
231
232         if (spw_close () == 0) {
233                 fprintf (stderr,
234                          _("%s: failure while writing changes to %s\n"),
235                          Prog, spw_dbname ());
236                 SYSLOG ((LOG_ERR, "failure while writing changes to %s", spw_dbname ()));
237                 fail_exit (E_FAILURE);
238         }
239         if (pw_close () == 0) {
240                 fprintf (stderr,
241                          _("%s: failure while writing changes to %s\n"),
242                          Prog, pw_dbname ());
243                 SYSLOG ((LOG_ERR, "failure while writing changes to %s", pw_dbname ()));
244                 fail_exit (E_FAILURE);
245         }
246
247         /* /etc/passwd- (backup file) */
248         if (chmod (PASSWD_FILE "-", 0600) != 0) {
249                 fprintf (stderr,
250                          _("%s: failed to change the mode of %s to 0600\n"),
251                          Prog, PASSWD_FILE "-");
252                 SYSLOG ((LOG_ERR, "failed to change the mode of %s to 0600", PASSWD_FILE "-"));
253                 /* continue */
254         }
255
256         if (pw_locked) {
257                 if (pw_unlock () == 0) {
258                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, pw_dbname ());
259                         SYSLOG ((LOG_ERR, "failed to unlock %s", pw_dbname ()));
260                         /* continue */
261                 }
262         }
263
264         if (spw_locked) {
265                 if (spw_unlock () == 0) {
266                         fprintf (stderr, _("%s: failed to unlock %s\n"), Prog, spw_dbname ());
267                         SYSLOG ((LOG_ERR, "failed to unlock %s", spw_dbname ()));
268                         /* continue */
269                 }
270         }
271
272         nscd_flush_cache ("passwd");
273
274         return E_SUCCESS;
275 }
276