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