Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / opie / opiepasswd.c
1 /* opiepasswd.c: Add/change an OTP password in the key database.
2
3 %%% portions-copyright-cmetz-96
4 Portions of this software are Copyright 1996-1999 by Craig Metz, All Rights
5 Reserved. The Inner Net License Version 2 applies to these portions of
6 the software.
7 You should have received a copy of the license with this software. If
8 you didn't get a copy, you may request one from <license@inner.net>.
9
10 Portions of this software are Copyright 1995 by Randall Atkinson and Dan
11 McDonald, All Rights Reserved. All Rights under this copyright are assigned
12 to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
13 License Agreement applies to this software.
14
15         History:
16
17         Modified by cmetz for OPIE 2.4. Use struct opie_key for key blocks.
18                 Use opiestrncpy().
19         Modified by cmetz for OPIE 2.32. Use OPIE_SEED_MAX instead of
20                 hard coding the length. Unlock user on failed lookup.
21         Modified by cmetz for OPIE 2.3. Got of some variables and made some
22                 local to where they're used. Split out the finishing code. Use
23                 opielookup() instead of opiechallenge() to find user. Three
24                 strikes on prompts. Use opiepasswd()'s new calling
25                 convention. Changed OPIE_PASS_{MAX,MIN} to
26                 OPIE_SECRET_{MAX,MIN}. Handle automatic reinits happenning
27                 below us. Got rid of unneeded headers. Use new opieatob8()
28                 return value convention. Added -f flag. Added SHA support.
29         Modified by cmetz for OPIE 2.22. Finally got rid of the lock
30                 filename kluge by implementing refcounts for locks.
31                 Use opiepasswd() to update key file. Error if we can't
32                 write to the key file. Check for minimum seed length.
33         Modified at NRL for OPIE 2.2. Changed opiestrip_crlf to
34                 opiestripcrlf. Check opiereadpass() return value.
35                 Minor optimization. Change calls to opiereadpass() to
36                 use echo arg. Use opiereadpass() where we can.
37                 Make everything static. Ifdef around some headers.
38                 Changed use of gethostname() to uname(). Got rid of
39                 the need for buf[]. Properly check return value of
40                 opieatob8. Check seed length. Always generate proper-
41                 length seeds.
42         Modified at NRL for OPIE 2.1. Minor autoconf changes.
43         Modified heavily at NRL for OPIE 2.0.
44         Written at Bellcore for the S/Key Version 1 software distribution
45                 (skeyinit.c).
46
47  $FreeBSD: src/contrib/opie/opiepasswd.c,v 1.1.1.2.6.3 2002/07/15 14:48:43 des Exp $
48 */
49 #include "opie_cfg.h"
50
51 #if HAVE_PWD_H
52 #include <pwd.h>
53 #endif /* HAVE_PWD_H */
54 #include <stdio.h>
55 #if HAVE_STRING_H
56 #include <string.h>
57 #endif /* HAVE_STRING_H */
58 #include <stdio.h>
59 #include <sys/types.h>
60 #if HAVE_UNISTD_H
61 #include <unistd.h>
62 #endif /* HAVE_UNISTD_H */
63 #if HAVE_STDLIB_H
64 #include <stdlib.h>
65 #endif /* HAVE_STDLIB_H */
66
67 #include "opie.h"
68
69 #define MODE_DEFAULT 0
70 #define MODE_CONSOLE 1
71 #define MODE_DISABLE 2
72
73 extern int optind;
74 extern char *optarg;
75
76 char *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" };
77 char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
78
79 static VOIDRET usage FUNCTION((myname), char *myname)
80 {
81   fprintf(stderr, "usage: %s [-v] [-h] [-c|-d] [-f] [-n initial_sequence_number]\n                            [-s seed] [username]\n", myname);
82   exit(1);
83 }
84
85 static VOIDRET finish FUNCTION((name), char *name)
86 {
87   struct opie opie;
88   char buf[OPIE_RESPONSE_MAX + 1];
89
90   if (name) {
91     if (opiechallenge(&opie, name, buf)) {
92       fprintf(stderr, "Error verifying database.\n");
93       finish(NULL);
94     }
95     printf("\nID %s ", opie.opie_principal);
96     if (opie.opie_val && (opie.opie_val[0] == '*')) {
97       printf("is disabled.\n");
98       finish(NULL);
99     }
100     printf("OTP key is %d %s\n", opie.opie_n, opie.opie_seed);
101     {
102       struct opie_otpkey key;
103
104       if (!opieatob8(&key, opie.opie_val)) {
105         fprintf(stderr, "Error verifying key -- possible database corruption.\n");
106         finish(NULL);
107       }
108       printf("%s\n", opiebtoe(buf, &key));
109     }
110   }
111
112   while(!opieunlock());
113   exit(name ? 0 : 1);
114 }
115
116 int main FUNCTION((argc, argv), int argc AND char *argv[])
117 {
118   struct opie opie;
119   int rval, n = 499, i, mode = MODE_DEFAULT, force = 0;
120   char seed[OPIE_SEED_MAX+1];
121   struct passwd *pp;
122
123   memset(seed, 0, sizeof(seed));
124
125   if (!(pp = getpwnam(getlogin()))) {
126     fprintf(stderr, "Who are you?");
127     return 1;
128   }
129
130   while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) {
131     switch (i) {
132     case 'v':
133       opieversion();
134     case 'f':
135 #if INSECURE_OVERRIDE
136       force = OPIEPASSWD_FORCE;
137 #else /* INSECURE_OVERRIDE */
138       fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
139 #endif /* INSECURE_OVERRIDE */
140       break;
141     case 'c':
142       mode = MODE_CONSOLE;
143       break;
144     case 'd':
145       mode = MODE_DISABLE;
146       break;
147     case 'n':
148       i = atoi(optarg);
149       if (!(i > 0 && i < 10000)) {
150         printf("Sequence numbers must be > 0 and < 10000\n");
151         finish(NULL);
152       }
153       n = i;
154       break;
155     case 's':
156       i = strlen(optarg);
157       if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) {
158         printf("Seeds must be between %d and %d characters long.\n",
159                OPIE_SEED_MIN, OPIE_SEED_MAX);
160         finish(NULL);
161       }
162       opiestrncpy(seed, optarg, sizeof(seed));
163       break;
164     default:
165       usage(argv[0]);
166     }
167   }
168
169   if (argc - optind >= 1) {
170     if (strcmp(argv[optind], pp->pw_name)) {
171       if (getuid()) {
172         printf("Only root can change others' passwords.\n");
173         exit(1);
174       }
175       if ((pp = getpwnam(argv[optind])) == NULL) {
176         printf("%s: user unknown.\n", argv[optind]);
177         exit(1);
178       }
179     }
180   }
181
182   opielock(pp->pw_name);
183   rval = opielookup(&opie, pp->pw_name);
184
185   switch (rval) {
186   case 0:
187     printf("Updating %s:\n", pp->pw_name);
188     break;
189   case 1:
190     printf("Adding %s:\n", pp->pw_name);
191     break;
192   case 2:
193     fprintf(stderr, "Error: Can't update key database.\n");
194     finish(NULL);
195   default:
196     fprintf(stderr, "Error reading key database\n");
197     finish(NULL);
198   }
199
200   if (seed[0]) {
201     i = strlen(seed);
202     if (i > OPIE_SEED_MAX) {
203       fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX);
204       finish(NULL);
205     }
206     if (i < OPIE_SEED_MIN) {
207       fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN);
208       finish(NULL);
209     }
210   } else {
211     if (!rval)
212       strcpy(seed, opie.opie_seed);
213
214     if (opienewseed(seed) < 0) {
215       fprintf(stderr, "Error updating seed.\n");
216       finish(NULL);
217     }
218   }
219
220   if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) {
221     fprintf(stderr, "You must use a different seed for the new OTP sequence.\n");
222     finish(NULL);
223   }
224   
225   switch(mode) {
226   case MODE_DEFAULT:
227     {
228       char tmp[OPIE_RESPONSE_MAX + 2];
229       
230       printf("You need the response from an OTP generator.\n");
231 #if DEBUG
232       if (!rval) {
233 #else /* DEBUG */
234       if (!rval && getuid()) {
235 #endif /* DEBUG */
236         char oseed[OPIE_SEED_MAX + 1];
237         int on;
238
239         if (opiechallenge(&opie, pp->pw_name, tmp)) {
240           fprintf(stderr, "Error issuing challenge.\n");
241           finish(NULL);
242         }
243         on = opiegetsequence(&opie);
244         {
245           char *c;
246           if (c = strrchr(tmp, ' '))
247             opiestrncpy(oseed, c + 1, sizeof(oseed));
248           else {
249 #if DEBUG
250             fprintf(stderr, "opiepasswd: bogus challenge\n");
251 #endif /* DEBUG */
252             finish(NULL);
253           }
254         }
255         printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp);
256         if (!opiereadpass(tmp, sizeof(tmp), 1))
257           tmp[0] = 0;
258         i = opieverify(&opie, tmp);
259         if (!tmp[0]) {
260           fprintf(stderr, "Error reading response.\n");
261           finish(NULL);
262         }
263         if (i) {
264           fprintf(stderr, "Error verifying response.\n");
265 #if DEBUG
266           fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i);
267 #endif /* DEBUG */
268           finish(NULL);
269         }
270         {
271           char nseed[OPIE_SEED_MAX + 1];
272           int nn;
273
274           if (opiechallenge(&opie, pp->pw_name, tmp)) {
275             fprintf(stderr, "Error verifying database.\n");
276             finish(NULL);
277           }
278
279           nn = opiegetsequence(&opie);
280           {
281             char *c;
282             if (c = strrchr(tmp, ' '))
283               opiestrncpy(nseed, c + 1, sizeof(nseed));
284             else {
285 #if DEBUG
286               fprintf(stderr, "opiepasswd: bogus challenge\n");
287 #endif /* DEBUG */
288               finish(NULL);
289             }
290           }
291
292           opieverify(&opie, "");
293           nn++;
294
295           if ((nn != on) || strcmp(oseed, nseed))
296             finish(pp->pw_name);
297         }
298       }
299       printf("New secret pass phrase:");
300       for (i = 0;; i++) {
301         if (i > 2)
302           finish(NULL);
303         printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed);
304         if (!opiereadpass(tmp, sizeof(tmp), 1)) {
305           fprintf(stderr, "Error reading response.\n");
306           finish(NULL);
307         }
308         if (tmp[0] == '?') {
309           printf("Enter the response from your OTP calculator: \n");
310           continue;
311         }
312         if (tmp[0] == '\0') {
313           fprintf(stderr, "Secret pass phrase unchanged.\n");
314           finish(NULL);
315         }
316         
317         if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp)))
318           finish(pp->pw_name);
319         
320         if (rval < 0) {
321           fprintf(stderr, "Error updating key database.\n");
322           finish(NULL);
323         }
324         printf("\tThat is not a valid OTP response.\n");
325       }
326     }
327     break;
328   case MODE_CONSOLE:
329     {
330       char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1];
331       /* Get user's secret password */
332       fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n");
333       fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n");
334       fprintf(stderr, "Then run opiepasswd without the -c parameter.\n");
335       if (opieinsecure() && !force) {
336         fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
337         if (force)
338           fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
339         else
340           finish(NULL);
341       };
342       printf("Using %s to compute responses.\n", algnames[MDX]);
343       if (!rval && getuid()) {
344         printf("Enter old secret pass phrase: ");
345         if (!opiereadpass(passwd, sizeof(passwd), 0)) {
346           fprintf(stderr, "Error reading secret pass phrase!\n");
347           finish(NULL);
348         }
349         if (!passwd[0]) {
350           fprintf(stderr, "Secret pass phrase unchanged.\n");
351           finish(NULL);
352         }
353         {
354           struct opie_otpkey key;
355           char tbuf[OPIE_RESPONSE_MAX + 1];
356           
357           if (opiekeycrunch(MDX, &key, opie.opie_seed, passwd) != 0) {
358             fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]);
359             finish(NULL);
360           }
361           memset(passwd, 0, sizeof(passwd));
362           i = opie.opie_n - 1;
363           while (i-- != 0)
364             opiehash(&key, MDX);
365           opiebtoe(tbuf, &key);
366           if (opieverify(&opie, tbuf)) {
367             fprintf(stderr, "Sorry.\n");
368             finish(NULL);
369           }
370         }
371       }
372       for (i = 0;; i++) {
373         if (i > 2)
374           finish(NULL);
375         printf("Enter new secret pass phrase: ");
376         if (!opiereadpass(passwd, sizeof(passwd), 0)) {
377           fprintf(stderr, "Error reading secret pass phrase.\n");
378           finish(NULL);
379         }
380         if (!passwd[0] || feof(stdin)) {
381           fprintf(stderr, "Secret pass phrase unchanged.\n");
382           finish(NULL);
383         }
384         if (opiepasscheck(passwd)) { 
385           memset(passwd, 0, sizeof(passwd));
386           fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
387           continue;
388         }
389         printf("Again new secret pass phrase: ");
390         if (!opiereadpass(passwd2, sizeof(passwd2), 0)) {
391           fprintf(stderr, "Error reading secret pass phrase.\n");
392           finish(NULL);
393         }
394         if (feof(stdin)) {
395           fprintf(stderr, "Secret pass phrase unchanged.\n");
396           finish(NULL);
397         }
398         if (!passwd[0] || !strcmp(passwd, passwd2))
399           break;
400         fprintf(stderr, "Sorry, no match.\n");
401       }
402       memset(passwd2, 0, sizeof(passwd2));
403       if (opiepasswd(&opie, 1 | force, pp->pw_name, n, seed, passwd)) {
404         fprintf(stderr, "Error updating key database.\n");
405         finish(NULL);
406       }
407       finish(pp->pw_name);
408     }
409   case MODE_DISABLE:
410     {
411       char tmp[4];
412       int i;
413
414       for (i = 0;; i++) {
415         if (i > 2)
416           finish(NULL);
417         
418         printf("Disable %s's OTP access? (yes or no) ", pp->pw_name);
419         if (!opiereadpass(tmp, sizeof(tmp), 1)) {
420           fprintf(stderr, "Error reading entry.\n");
421           finish(NULL);
422         }
423         if (!strcmp(tmp, "no"))
424           finish(NULL);
425         if (!strcmp(tmp, "yes")) {
426           if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) {
427             fprintf(stderr, "Error updating key database.\n");
428             finish(NULL);
429           }
430           finish(pp->pw_name);
431         }
432       }
433     }
434   }
435 }