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