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