]> arthur.barton.de Git - netatalk.git/blob - bin/afppasswd/afppasswd.c
Warning fixes.
[netatalk.git] / bin / afppasswd / afppasswd.c
1 /* 
2  * $Id: afppasswd.c,v 1.7 2001-07-31 19:49:02 srittau Exp $
3  *
4  * Copyright 1999 (c) Adrian Sun (asun@u.washington.edu)
5  * All Rights Reserved. See COPYRIGHT.
6  *
7  * format of the password file:
8  * name:****************:****************:********
9  *      password         last login date  failed usage count
10  *
11  * ***'s are illegal. they're just place holders for hex values. hex
12  * values that represent actual numbers are in network byte order.
13  *
14  * last login date is currently a 4-byte number representing seconds
15  * since 1970 (UTC). there's enough space to extend it to 8 bytes.
16  *
17  * the last two fields aren't currently used by the randnum uams.
18  *
19  * root syntax: afppasswd [-c] [-a] [-p path] [-f] [username]
20  * user syntax: afppasswd 
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif /* HAVE_CONFIG_H */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif /* HAVE_UNISTD_H */
32 #include <ctype.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/param.h>
37 #ifdef HAVE_FCNTL_H
38 #include <fcntl.h>
39 #endif /* HAVE_FCNTL_H */
40 #include <pwd.h>
41
42 #include <netatalk/endian.h>
43
44 #ifdef UAM_RNDNUM
45 #include <des.h>
46
47 #ifdef USE_CRACKLIB
48 #include <crack.h>
49 #endif /* USE_CRACKLIB */
50
51 #define OPT_ISROOT  (1 << 0)
52 #define OPT_CREATE  (1 << 1)
53 #define OPT_FORCE   (1 << 2)
54 #define OPT_ADDUSER (1 << 3)
55 #define OPT_NOCRACK (1 << 4)
56
57 #define PASSWD_ILLEGAL '*'
58
59 #define FORMAT  ":****************:****************:********\n"
60 #define FORMAT_LEN 44
61 #define OPTIONS "cafnu:p:"
62 #define UID_START 100
63
64 #define HEXPASSWDLEN 16
65 #define PASSWDLEN 8
66
67 static char buf[MAXPATHLEN + 1];
68
69 /* if newpwd is null, convert buf from hex to binary. if newpwd isn't
70  * null, convert newpwd to hex and save it in buf. */
71 #define unhex(x)  (isdigit(x) ? (x) - '0' : toupper(x) + 10 - 'A')
72 static void convert_passwd(char *buf, char *newpwd, const int keyfd)
73 {
74   u_int8_t key[HEXPASSWDLEN];
75   Key_schedule schedule;
76   int i, j;
77
78   if (!newpwd) {
79     /* convert to binary */
80     for (i = j = 0; i < sizeof(key); i += 2, j++)
81       buf[j] = (unhex(buf[i]) << 4) | unhex(buf[i + 1]);
82     if (j <= DES_KEY_SZ)
83       memset(buf + j, 0, sizeof(key) - j);
84   }
85
86   if (keyfd > -1) {
87     lseek(keyfd, 0, SEEK_SET);
88     read(keyfd, key, sizeof(key));
89     /* convert to binary */
90     for (i = j = 0; i < sizeof(key); i += 2, j++)
91       key[j] = (unhex(key[i]) << 4) | unhex(key[i + 1]);
92     if (j <= DES_KEY_SZ)
93       memset(key + j, 0, sizeof(key) - j);
94     key_sched((C_Block *) key, schedule);
95     memset(key, 0, sizeof(key));   
96     if (newpwd) {
97         ecb_encrypt((C_Block *) newpwd, (C_Block *) newpwd, schedule,
98                     DES_ENCRYPT);
99     } else {
100       /* decrypt the password */
101       ecb_encrypt((C_Block *) buf, (C_Block *) buf, schedule, DES_DECRYPT);
102     }
103     memset(schedule, 0, sizeof(schedule));      
104   }
105
106   if (newpwd) {
107     const unsigned char hextable[] = "0123456789ABCDEF";
108
109     /* convert to hex */
110     for (i = j = 0; i < DES_KEY_SZ; i++, j += 2) {
111       buf[j] = hextable[(newpwd[i] & 0xF0) >> 4];
112       buf[j + 1] = hextable[newpwd[i] & 0x0F];
113     }
114   }
115 }
116
117 /* this matches the code in uam_randnum.c */
118 static int update_passwd(const char *path, const char *name, int flags)
119 {
120   char password[PASSWDLEN + 1], *p, *passwd;
121   FILE *fp;
122   off_t pos;
123   int keyfd = -1, err = 0;
124
125   if ((fp = fopen(path, "r+")) == NULL) {
126     fprintf(stderr, "afppasswd: can't open %s\n", path);
127     return -1;
128   }
129
130   /* open the key file if it exists */
131   strcpy(buf, path);
132   if (strlen(path) < sizeof(buf) - 5) {
133     strcat(buf, ".key");
134     keyfd = open(buf, O_RDONLY);
135   } 
136
137   pos = ftell(fp);
138   memset(buf, 0, sizeof(buf));
139   while (fgets(buf, sizeof(buf), fp)) {
140     if ((p = strchr(buf, ':'))) {
141       /* check for a match */
142       if (strncmp(buf, name, p - buf) == 0) {
143         p++;
144         if (!(flags & OPT_ISROOT) && (*p == PASSWD_ILLEGAL)) {
145           fprintf(stderr, "Your password is disabled. Please see your administrator.\n");
146           break;
147         }
148         goto found_entry;
149       }
150     }
151     pos = ftell(fp);
152     memset(buf, 0, sizeof(buf));
153   }
154
155   if (flags & OPT_ADDUSER) {
156     strcpy(buf, name);
157     strcat(buf, FORMAT);
158     p = strchr(buf, ':') + 1;
159     fwrite(buf, strlen(buf), 1, fp);
160   } else {
161     fprintf(stderr, "afppasswd: can't find %s in %s\n", name, path);
162     err = -1;
163     goto update_done;
164   }
165
166 found_entry:
167   /* need to verify against old password */
168   if ((flags & OPT_ISROOT) == 0) {
169     passwd = getpass("Enter OLD AFP password: ");
170     convert_passwd(p, NULL, keyfd);
171     if (strncmp(passwd, p, PASSWDLEN)) {
172       fprintf(stderr, "afppasswd: invalid password.\n");
173       err = -1;
174       goto update_done;
175     }
176   }
177
178   /* new password */
179   passwd = getpass("Enter NEW AFP password: ");
180   memcpy(password, passwd, sizeof(password));
181   password[PASSWDLEN] = '\0';
182 #ifdef USE_CRACKLIB
183   if (!(flags & OPT_NOCRACK)) {
184     if (passwd = FascistCheck(password, _PATH_CRACKLIB)) { 
185         fprintf(stderr, "Error: %s\n", passwd);
186         err = -1;
187         goto update_done;
188     } 
189   }
190 #endif /* USE_CRACKLIB */
191
192   passwd = getpass("Enter NEW AFP password again: ");
193   if (strcmp(passwd, password) == 0) {
194      struct flock lock;
195      int fd = fileno(fp);
196
197      convert_passwd(p, password, keyfd);
198      lock.l_type = F_WRLCK;
199      lock.l_start = pos;
200      lock.l_len = 1;
201      lock.l_whence = SEEK_SET;
202      fseek(fp, pos, SEEK_SET);
203      fcntl(fd, F_SETLKW, &lock);
204      fwrite(buf, p - buf + HEXPASSWDLEN, 1, fp);
205      lock.l_type = F_UNLCK;
206      fcntl(fd, F_SETLK, &lock);
207      printf("afppasswd: updated password.\n");
208
209   } else {
210     fprintf(stderr, "afppasswd: passwords don't match!\n");
211     err = -1;
212   }
213
214 update_done:
215   if (keyfd > -1)
216     close(keyfd);
217   fclose(fp);
218   return err;
219 }
220
221
222 /* creates a file with all the password entries */
223 static int create_file(const char *path, uid_t minuid)
224 {
225   struct passwd *pwd;
226   int fd, len, err = 0;
227
228   
229   if ((fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600)) < 0) {
230     fprintf(stderr, "afppasswd: can't create %s\n", path);
231     return -1;
232   }
233
234   setpwent();
235   while ((pwd = getpwent())) {
236     if (pwd->pw_uid < minuid)
237       continue;
238     /* a little paranoia */
239     if (strlen(pwd->pw_name) + FORMAT_LEN > sizeof(buf) - 1)
240       continue;
241     strcpy(buf, pwd->pw_name);
242     strcat(buf, FORMAT);
243     len = strlen(buf);
244     if (write(fd, buf, len) != len) {
245       fprintf(stderr, "afppasswd: problem writing to %s: %m\n", path);
246       err = -1;
247       break;
248     }
249   }
250   endpwent();
251   close(fd);
252
253   return err;
254 }
255
256
257 int main(int argc, char **argv)
258 {
259   struct stat st;
260   int flags;
261   uid_t uid_min = UID_START, uid;
262   char *path = _PATH_AFPDPWFILE;
263   int i, err = 0;
264
265   extern char *optarg;
266   extern int optind;
267
268   flags = ((uid = getuid()) == 0) ? OPT_ISROOT : 0;
269
270   if (((flags & OPT_ISROOT) == 0) && (argc > 1)) {
271     fprintf(stderr, "Usage: afppasswd [-acfn] [-u minuid] [-p path] [username]\n");
272     fprintf(stderr, "  -a        add a new user\n");
273     fprintf(stderr, "  -c        create and initialize password file or specific user\n");
274     fprintf(stderr, "  -f        force an action\n");
275 #ifdef USE_CRACKLIB
276     fprintf(stderr, "  -n        disable cracklib checking of passwords\n");
277 #endif /* USE_CRACKLIB */
278     fprintf(stderr, "  -u uid    minimum uid to use, defaults to 100\n");
279     fprintf(stderr, "  -p path   path to afppasswd file\n");
280     return -1;
281   }
282
283   while ((i = getopt(argc, argv, OPTIONS)) != EOF) {
284     switch (i) {
285     case 'c': /* create and initialize password file or specific user */
286       flags |= OPT_CREATE;
287       break;
288     case 'a': /* add a new user */
289       flags |= OPT_ADDUSER;
290       break;
291     case 'f': /* force an action */
292       flags |= OPT_FORCE;
293       break;
294     case 'u':  /* minimum uid to use. default is 100 */
295       uid_min = atoi(optarg);
296       break;
297 #ifdef USE_CRACKLIB
298     case 'n': /* disable CRACKLIB check */
299       flags |= OPT_NOCRACK;
300       break;
301 #endif /* USE_CRACKLIB */
302     case 'p': /* path to afppasswd file */
303       path = optarg;
304       break;
305     default:
306       err++;
307       break;
308     }
309   }
310   
311   if (err || (optind + ((flags & OPT_CREATE) ? 0 : 
312                         (flags & OPT_ISROOT)) != argc)) {
313 #ifdef USE_CRACKLIB
314     fprintf(stderr, "Usage: afppasswd [-acfn] [-u minuid] [-p path] [username]\n");
315 #else /* USE_CRACKLIB */
316     fprintf(stderr, "Usage: afppasswd [-acf] [-u minuid] [-p path] [username]\n");
317 #endif /* USE_CRACKLIB */
318     fprintf(stderr, "  -a        add a new user\n");
319     fprintf(stderr, "  -c        create and initialize password file or specific user\n");
320     fprintf(stderr, "  -f        force an action\n");
321 #ifdef USE_CRACKLIB
322     fprintf(stderr, "  -n        disable cracklib checking of passwords\n");
323 #endif /* USE_CRACKLIB */
324     fprintf(stderr, "  -u uid    minimum uid to use, defaults to 100\n");
325     fprintf(stderr, "  -p path   path to afppasswd file\n");
326     return -1;
327   }
328
329   i = stat(path, &st);
330   if (flags & OPT_CREATE) {
331     if ((flags & OPT_ISROOT) == 0) {
332       fprintf(stderr, "afppasswd: only root can create the password file.\n");
333       return -1;
334     }
335
336     if (!i && ((flags & OPT_FORCE) == 0)) {
337       fprintf(stderr, "afppasswd: password file already exists.\n");
338       return -1;
339     }
340     return create_file(path, uid_min);
341
342   } else {
343     struct passwd *pwd = NULL;
344
345     if (i < 0) {
346       fprintf(stderr, "afppasswd: %s doesn't exist.\n", path);
347       return -1;
348     }
349     
350     /* if we're root, we need to specify the username */
351     pwd = (flags & OPT_ISROOT) ? getpwnam(argv[optind]) : getpwuid(uid);
352     if (pwd)
353       return update_passwd(path, pwd->pw_name, flags);
354
355     fprintf(stderr, "afppasswd: can't get password entry.\n");
356     return -1;
357   }
358 }
359 #else /* UAM_RNDNUM */
360
361 main(int argc, char **argv)
362 {
363   fprintf(stderr, "afppasswd is only useful if you're using centralized passwords\n");
364   fprintf(stderr, "for the Random Number authentication methods.\n");
365   return -1;
366 }
367
368 #endif /* UAM_RNDNUM */