]> git.ipfire.org Git - thirdparty/util-linux.git/blob - login-utils/passwd.c
Imported from util-linux-2.10f tarball.
[thirdparty/util-linux.git] / login-utils / passwd.c
1 /*
2 * passwd.c - change password on an account
3 *
4 * Initially written for Linux by Peter Orbaek <poe@daimi.aau.dk>
5 * Currently maintained at ftp://ftp.daimi.aau.dk/pub/linux/poe/
6 *
7 * Hacked by Alvaro Martinez Echevarria, alvaro@enano.etsit.upm.es,
8 * to allow peaceful coexistence with yp. Nov 94.
9 *
10 * Hacked to allow root to set passwd from command line.
11 * by Arpad Magossanyi (mag@tas.vein.hu)
12 */
13
14 /*
15 * Sun Oct 15 13:18:34 1995 Martin Schulze <joey@finlandia.infodrom.north.de>
16 *
17 * I have completely rewritten the whole argument handling (what?)
18 * to support two things. First I wanted "passwd $user $pw" to
19
20 (a very bad idea; command lines are visible to people doing ps
21 or running a background job that just collects all command lines)
22
23 * work and second I wanted simplicity checks to be done for
24 * root, too. Only root can turn this off using the -f
25 * switch. Okay, I started with this to support -V version
26 * information, but one thing comes to the next. *sigh*
27 * In a later step perhaps we'll be able to support shadow
28 * passwords. (?)
29 *
30 * I have also included a DEBUG mode (-DDEBUG) to test the
31 * argument handling _without_ any write attempt to
32 * /etc/passwd.
33 *
34 * If you're paranoid about security on your system, you may want
35 * to add -DLOGALL to CFLAGS. This will turn on additional syslog
36 * logging of every password change. (user changes are logged as
37 * auth.notice, but changing root's password is logged as
38 * auth.warning. (Of course, the password itself is not logged.)
39 */
40
41 /* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
42 * - added Native Language Support
43 * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
44 * - fixed strerr(errno) in gettext calls
45 */
46
47 /*
48 * Usage: passwd [username [password]]
49 * Only root may use the one and two argument forms.
50 */
51
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <stdio.h>
55 #include <unistd.h>
56 #include <stdarg.h>
57 #include <termios.h>
58 #include <getopt.h>
59 #include <malloc.h>
60 #include <fcntl.h>
61 #include <pwd.h>
62 #include <ctype.h>
63 #include <time.h>
64 #include <string.h>
65 #include <errno.h>
66 #include <sys/resource.h>
67 #include "my_crypt.h"
68 #include "nls.h"
69 #include "env.h"
70
71 #ifndef _PATH_CHFN
72 # define _PATH_CHFN "/usr/bin/chfn"
73 # define _PATH_CHSH "/usr/bin/chsh"
74 #endif
75
76 #define LOGALL
77
78 #ifdef LOGALL
79 #include <syslog.h>
80 #endif /* LOGALL */
81
82 extern int is_local(char *); /* islocal.c */
83 extern int setpwnam(struct passwd *); /* setpwnam.c */
84
85 #define ascii_to_bin(c) ((c)>='a'?(c-59):(c)>='A'?((c)-53):(c)-'.')
86 #define bin_to_ascii(c) ((c)>=38?((c)-38+'a'):(c)>=12?((c)-12+'A'):(c)+'.')
87
88 static void
89 pexit(char *str, ...)
90 {
91 va_list vlst;
92
93 va_start(vlst, str);
94 vfprintf(stderr, str, vlst);
95 fprintf(stderr, ": ");
96 perror("");
97 va_end(vlst);
98 exit(1);
99 }
100
101 /*
102 * Do various checks for stupid passwords here...
103 *
104 * This would probably be the best place for checking against
105 * dictionaries. :-)
106 */
107
108 int check_passwd_string(char *passwd, char *string)
109 {
110 int r;
111 char *p, *q;
112
113 r = 0;
114 /* test for string at the beginning of passwd */
115 for (p = passwd, q = string; *q && *p; q++, p++) {
116 if(tolower(*p) != tolower(*q)) {
117 r++;
118 break;
119 }
120 }
121
122 /* test for reverse string at the beginning of passwd */
123 for (p = passwd, q = string + strlen(string)-1;
124 *p && q >= string; p++, q--) {
125 if(tolower(*p) != tolower(*q)) {
126 r++;
127 break;
128 }
129 }
130
131 /* test for string at the end of passwd */
132 for (p = passwd + strlen(passwd)-1, q = string + strlen(string)-1;
133 q >= string && p >= passwd; q--, p--) {
134 if(tolower(*p) != tolower(*q)) {
135 r++;
136 break;
137 }
138 }
139
140 /* test for reverse string at the beginning of passwd */
141 for (p = passwd + strlen(passwd)-1, q = string;
142 p >= passwd && *q; p--, q++) {
143 if(tolower(*p) != tolower(*q)) {
144 r++;
145 break;
146 }
147 }
148
149 if (r != 4) {
150 return 0;
151 }
152 return 1;
153 }
154
155 int check_passwd(char *passwd, char *oldpasswd, char *user, char *gecos)
156 {
157 int ucase, lcase, digit, other;
158 char *c, *g, *p;
159
160 if ( (strlen(passwd) < 6) ) {
161 printf(_("The password must have at least 6 characters, try again.\n"));
162 return 0;
163 }
164
165 other = digit = ucase = lcase = 0;
166 for (p = passwd; *p; p++) {
167 ucase = ucase || isupper(*p);
168 lcase = lcase || islower(*p);
169 digit = digit || isdigit(*p);
170 other = other || !isalnum(*p);
171 }
172
173 if ( (other + digit + ucase + lcase) < 2) {
174 printf(_("The password must contain characters out of two of the following\n"));
175 printf(_("classes: upper and lower case letters, digits and non alphanumeric\n"));
176 printf(_("characters. See passwd(1) for more information.\n"));
177 return 0;
178 }
179
180 if ( oldpasswd[0] && !strncmp(oldpasswd, crypt(passwd, oldpasswd), 13) ) {
181 printf(_("You cannot reuse the old password.\n"));
182 return 0;
183 }
184
185 if ( !check_passwd_string(passwd, user) ) {
186 printf(_("Please don't use something like your username as password!\n"));
187 return 0;
188 }
189
190 /* check against realname */
191 if ( (c = index(gecos, ',')) ) {
192 if ( c-gecos && (g = (char *)malloc (c-gecos+1)) ) {
193 strncpy (g, gecos, c-gecos);
194 g[c-gecos] = 0;
195 while ( (c=rindex(g, ' ')) ) {
196 if ( !check_passwd_string(passwd, c+1) ) {
197 printf(_("Please don't use something like your realname as password!\n"));
198 free (g);
199 return 0;
200 }
201 *c = '\0';
202 } /* while */
203 if ( !check_passwd_string(passwd, g) ) {
204 printf(_("Please don't use something like your realname as password!\n"));
205 free (g);
206 return 0;
207 }
208 free (g);
209 } /* if malloc */
210 }
211
212 /*
213 * if ( !check_password_dict(passwd) ) ...
214 */
215
216 return 1; /* fine */
217 }
218
219 void usage()
220 {
221 printf (_("Usage: passwd [username [password]]\n"));
222 printf(_("Only root may use the one and two argument forms.\n"));
223 }
224
225 int
226 main(argc, argv)
227 int argc;
228 char *argv[];
229 {
230 struct passwd *pe;
231 uid_t gotuid = getuid();
232 char *pwdstr = NULL, *cryptstr, *oldstr;
233 char pwdstr1[10];
234 char *user;
235 time_t tm;
236 char salt[2];
237 int force_passwd = 0;
238 int silent = 0;
239 char c;
240 int opt_index;
241 int fullname = 0, shell = 0;
242 static const struct option long_options[] =
243 {
244 {"fullname", no_argument, 0, 'f'},
245 {"shell", no_argument, 0, 's'},
246 {"force", no_argument, 0, 'o'},
247 {"quiet", no_argument, 0, 'q'},
248 {"silent", no_argument, 0, 'q'},
249 {"version", no_argument, 0, 'v'},
250 {0, 0, 0, 0}
251 };
252
253 sanitize_env();
254 setlocale(LC_ALL, "");
255 bindtextdomain(PACKAGE, LOCALEDIR);
256 textdomain(PACKAGE);
257
258 optind = 0;
259 while ( (c = getopt_long(argc, argv, "foqsvV", long_options, &opt_index)) != -1 ) {
260 switch (c) {
261 case 'f':
262 fullname = 1;
263 break;
264 case 's':
265 shell = 1;
266 break;
267 case 'o':
268 force_passwd = 1;
269 break;
270 case 'q':
271 silent = 1;
272 break;
273 case 'V':
274 case 'v':
275 printf("%s\n", util_linux_version);
276 exit(0);
277 default:
278 fprintf(stderr, _("Usage: passwd [-foqsvV] [user [password]]\n"));
279 exit(1);
280 } /* switch (c) */
281 } /* while */
282
283 if (fullname || shell) {
284 char *args[100];
285 int i, j, errsv;
286
287 setuid(getuid()); /* drop special privs. */
288 if (fullname)
289 args[0] = _PATH_CHFN;
290 else
291 args[0] = _PATH_CHSH;
292
293 for (i = optind, j = 1; (i < argc) && (j < 99); i++, j++)
294 args[j] = argv[i];
295
296 args[j] = NULL;
297 execv(args[0], args);
298 errsv = errno;
299 fprintf(stderr, _("Can't exec %s: %s\n"), args[0], strerror(errsv));
300 exit(1);
301 }
302
303 switch (argc - optind) {
304 case 0:
305 /* Why use getlogin()? Some systems allow having several
306 usernames with the same uid, especially several root accounts.
307 One changes the password for the username, not the uid. */
308 if ( !(user = getlogin()) || !*user ) {
309 if ( !(pe = getpwuid( getuid() )) ) {
310 pexit(_("Cannot find login name"));
311 } else
312 user = pe->pw_name;
313 }
314 break;
315 case 1:
316 if(gotuid) {
317 printf(_("Only root can change the password for others.\n"));
318 exit (1);
319 } else
320 user = argv[optind];
321 break;
322 case 2:
323 if(gotuid) {
324 printf(_("Only root can change the password for others.\n"));
325 exit(1);
326 } else {
327 user = argv[optind];
328 pwdstr = argv[optind+1];
329 }
330 break;
331 default:
332 printf(_("Too many arguments.\n"));
333 exit (1);
334 } /* switch */
335
336 if(!(pe = getpwnam(user))) {
337 pexit(_("Can't find username anywhere. Is `%s' really a user?"), user);
338 }
339
340 if (!(is_local(user))) {
341 puts(_("Sorry, I can only change local passwords. Use yppasswd instead."));
342 exit(1);
343 }
344
345 /* if somebody got into changing utmp... */
346 if(gotuid && gotuid != pe->pw_uid) {
347 puts(_("UID and username does not match, imposter!"));
348 exit(1);
349 }
350
351 if ( !silent )
352 printf( _("Changing password for %s\n"), user );
353
354 if ( (gotuid && pe->pw_passwd && pe->pw_passwd[0])
355 || (!gotuid && !strcmp(user,"root")) ) {
356 oldstr = getpass(_("Enter old password: "));
357 if(strncmp(pe->pw_passwd, crypt(oldstr, pe->pw_passwd), 13)) {
358 puts(_("Illegal password, imposter."));
359 exit(1);
360 }
361 }
362
363 if ( pwdstr ) { /* already set on command line */
364 if ( !force_passwd && !check_passwd(pwdstr, pe->pw_passwd, user, pe->pw_gecos) )
365 exit (1);
366 } else {
367 /* password not set on command line by root, ask for it ... */
368
369 redo_it:
370 pwdstr = getpass(_("Enter new password: "));
371 if (pwdstr[0] == '\0') {
372 puts(_("Password not changed."));
373 exit(1);
374 }
375
376 if ( (gotuid || (!gotuid && !force_passwd))
377 && !check_passwd(pwdstr, pe->pw_passwd, user, pe->pw_gecos) )
378 goto redo_it;
379
380 strncpy(pwdstr1, pwdstr, 9);
381 pwdstr1[9] = 0;
382 pwdstr = getpass(_("Re-type new password: "));
383
384 if(strncmp(pwdstr, pwdstr1, 8)) {
385 puts(_("You misspelled it. Password not changed."));
386 exit(1);
387 }
388 } /* pwdstr i.e. password set on command line */
389
390 time(&tm); tm ^= getpid();
391 salt[0] = bin_to_ascii(tm & 0x3f);
392 salt[1] = bin_to_ascii((tm >> 6) & 0x3f);
393 cryptstr = crypt(pwdstr, salt);
394
395 if (pwdstr[0] == 0) cryptstr = "";
396
397 #ifdef LOGALL
398 openlog("passwd", 0, LOG_AUTH);
399 if (gotuid)
400 syslog(LOG_NOTICE,_("password changed, user %s"),user);
401 else {
402 if ( !strcmp(user, "root") )
403 syslog(LOG_WARNING,_("ROOT PASSWORD CHANGED"));
404 else
405 syslog(LOG_NOTICE,_("password changed by root, user %s"),user);
406 }
407 closelog();
408 #endif /* LOGALL */
409
410 pe->pw_passwd = cryptstr;
411 #ifdef DEBUG
412 printf (_("calling setpwnam to set password.\n"));
413 #else
414 if (setpwnam( pe ) < 0) {
415 perror( "setpwnam" );
416 printf( _("Password *NOT* changed. Try again later.\n" ));
417 exit( 1 );
418 }
419 #endif
420
421 if ( !silent )
422 printf(_("Password changed.\n"));
423 exit(0);
424 }