Merge from vendor branch NTPD:
[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  $DragonFly: src/contrib/opie/opiepasswd.c,v 1.2 2003/06/17 04:24:05 dillon Exp $
49 */
50 #include "opie_cfg.h"
51
52 #if HAVE_PWD_H
53 #include <pwd.h>
54 #endif /* HAVE_PWD_H */
55 #include <stdio.h>
56 #if HAVE_STRING_H
57 #include <string.h>
58 #endif /* HAVE_STRING_H */
59 #include <stdio.h>
60 #include <sys/types.h>
61 #if HAVE_UNISTD_H
62 #include <unistd.h>
63 #endif /* HAVE_UNISTD_H */
64 #if HAVE_STDLIB_H
65 #include <stdlib.h>
66 #endif /* HAVE_STDLIB_H */
67
68 #include "opie.h"
69
70 #define MODE_DEFAULT 0
71 #define MODE_CONSOLE 1
72 #define MODE_DISABLE 2
73
74 extern int optind;
75 extern char *optarg;
76
77 char *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" };
78 char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
79
80 static VOIDRET usage FUNCTION((myname), char *myname)
81 {
82   fprintf(stderr, "usage: %s [-v] [-h] [-c|-d] [-f] [-n initial_sequence_number]\n                            [-s seed] [username]\n", myname);
83   exit(1);
84 }
85
86 static VOIDRET finish FUNCTION((name), char *name)
87 {
88   struct opie opie;
89   char buf[OPIE_RESPONSE_MAX + 1];
90
91   if (name) {
92     if (opiechallenge(&opie, name, buf)) {
93       fprintf(stderr, "Error verifying database.\n");
94       finish(NULL);
95     }
96     printf("\nID %s ", opie.opie_principal);
97     if (opie.opie_val && (opie.opie_val[0] == '*')) {
98       printf("is disabled.\n");
99       finish(NULL);
100     }
101     printf("OTP key is %d %s\n", opie.opie_n, opie.opie_seed);
102     {
103       struct opie_otpkey key;
104
105       if (!opieatob8(&key, opie.opie_val)) {
106         fprintf(stderr, "Error verifying key -- possible database corruption.\n");
107         finish(NULL);
108       }
109       printf("%s\n", opiebtoe(buf, &key));
110     }
111   }
112
113   while(!opieunlock());
114   exit(name ? 0 : 1);
115 }
116
117 int main FUNCTION((argc, argv), int argc AND char *argv[])
118 {
119   struct opie opie;
120   int rval, n = 499, i, mode = MODE_DEFAULT, force = 0;
121   char seed[OPIE_SEED_MAX+1];
122   struct passwd *pp;
123
124   memset(seed, 0, sizeof(seed));
125
126   if (!(pp = getpwnam(getlogin()))) {
127     fprintf(stderr, "Who are you?");
128     return 1;
129   }
130
131   while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) {
132     switch (i) {
133     case 'v':
134       opieversion();
135     case 'f':
136 #if INSECURE_OVERRIDE
137       force = OPIEPASSWD_FORCE;
138 #else /* INSECURE_OVERRIDE */
139       fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
140 #endif /* INSECURE_OVERRIDE */
141       break;
142     case 'c':
143       mode = MODE_CONSOLE;
144       break;
145     case 'd':
146       mode = MODE_DISABLE;
147       break;
148     case 'n':
149       i = atoi(optarg);
150       if (!(i > 0 && i < 10000)) {
151         printf("Sequence numbers must be > 0 and < 10000\n");
152         finish(NULL);
153       }
154       n = i;
155       break;
156     case 's':
157       i = strlen(optarg);
158       if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) {
159         printf("Seeds must be between %d and %d characters long.\n",
160                OPIE_SEED_MIN, OPIE_SEED_MAX);
161         finish(NULL);
162       }
163       opiestrncpy(seed, optarg, sizeof(seed));
164       break;
165     default:
166       usage(argv[0]);
167     }
168   }
169
170   if (argc - optind >= 1) {
171     if (strcmp(argv[optind], pp->pw_name)) {
172       if (getuid()) {
173         printf("Only root can change others' passwords.\n");
174         exit(1);
175       }
176       if ((pp = getpwnam(argv[optind])) == NULL) {
177         printf("%s: user unknown.\n", argv[optind]);
178         exit(1);
179       }
180     }
181   }
182
183   opielock(pp->pw_name);
184   rval = opielookup(&opie, pp->pw_name);
185
186   switch (rval) {
187   case 0:
188     printf("Updating %s:\n", pp->pw_name);
189     break;
190   case 1:
191     printf("Adding %s:\n", pp->pw_name);
192     break;
193   case 2:
194     fprintf(stderr, "Error: Can't update key database.\n");
195     finish(NULL);
196   default:
197     fprintf(stderr, "Error reading key database\n");
198     finish(NULL);
199   }
200
201   if (seed[0]) {
202     i = strlen(seed);
203     if (i > OPIE_SEED_MAX) {
204       fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX);
205       finish(NULL);
206     }
207     if (i < OPIE_SEED_MIN) {
208       fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN);
209       finish(NULL);
210     }
211   } else {
212     if (!rval)
213       strcpy(seed, opie.opie_seed);
214
215     if (opienewseed(seed) < 0) {
216       fprintf(stderr, "Error updating seed.\n");
217       finish(NULL);
218     }
219   }
220
221   if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) {
222     fprintf(stderr, "You must use a different seed for the new OTP sequence.\n");
223     finish(NULL);
224   }
225   
226   switch(mode) {
227   case MODE_DEFAULT:
228     {
229       char tmp[OPIE_RESPONSE_MAX + 2];
230       
231       printf("You need the response from an OTP generator.\n");
232 #if DEBUG
233       if (!rval) {
234 #else /* DEBUG */
235       if (!rval && getuid()) {
236 #endif /* DEBUG */
237         char oseed[OPIE_SEED_MAX + 1];
238         int on;
239
240         if (opiechallenge(&opie, pp->pw_name, tmp)) {
241           fprintf(stderr, "Error issuing challenge.\n");
242           finish(NULL);
243         }
244         on = opiegetsequence(&opie);
245         {
246           char *c;
247           if (c = strrchr(tmp, ' '))
248             opiestrncpy(oseed, c + 1, sizeof(oseed));
249           else {
250 #if DEBUG
251             fprintf(stderr, "opiepasswd: bogus challenge\n");
252 #endif /* DEBUG */
253             finish(NULL);
254           }
255         }
256         printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp);
257         if (!opiereadpass(tmp, sizeof(tmp), 1))
258           tmp[0] = 0;
259         i = opieverify(&opie, tmp);
260         if (!tmp[0]) {
261           fprintf(stderr, "Error reading response.\n");
262           finish(NULL);
263         }
264         if (i) {
265           fprintf(stderr, "Error verifying response.\n");
266 #if DEBUG
267           fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i);
268 #endif /* DEBUG */
269           finish(NULL);
270         }
271         {
272           char nseed[OPIE_SEED_MAX + 1];
273           int nn;
274
275           if (opiechallenge(&opie, pp->pw_name, tmp)) {
276             fprintf(stderr, "Error verifying database.\n");
277             finish(NULL);
278           }
279
280           nn = opiegetsequence(&opie);
281           {
282             char *c;
283             if (c = strrchr(tmp, ' '))
284               opiestrncpy(nseed, c + 1, sizeof(nseed));
285             else {
286 #if DEBUG
287               fprintf(stderr, "opiepasswd: bogus challenge\n");
288 #endif /* DEBUG */
289               finish(NULL);
290             }
291           }
292
293           opieverify(&opie, "");
294           nn++;
295
296           if ((nn != on) || strcmp(oseed, nseed))
297             finish(pp->pw_name);
298         }
299       }
300       printf("New secret pass phrase:");
301       for (i = 0;; i++) {
302         if (i > 2)
303           finish(NULL);
304         printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed);
305         if (!opiereadpass(tmp, sizeof(tmp), 1)) {
306           fprintf(stderr, "Error reading response.\n");
307           finish(NULL);
308         }
309         if (tmp[0] == '?') {
310           printf("Enter the response from your OTP calculator: \n");
311           continue;
312         }
313         if (tmp[0] == '\0') {
314           fprintf(stderr, "Secret pass phrase unchanged.\n");
315           finish(NULL);
316         }
317         
318         if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp)))
319           finish(pp->pw_name);
320         
321         if (rval < 0) {
322           fprintf(stderr, "Error updating key database.\n");
323           finish(NULL);
324         }
325         printf("\tThat is not a valid OTP response.\n");
326       }
327     }
328     break;
329   case MODE_CONSOLE:
330     {
331       char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1];
332       /* Get user's secret password */
333       fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n");
334       fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n");
335       fprintf(stderr, "Then run opiepasswd without the -c parameter.\n");
336       if (opieinsecure() && !force) {
337         fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
338         if (force)
339           fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
340         else
341           finish(NULL);
342       };
343       printf("Using %s to compute responses.\n", algnames[MDX]);
344       if (!rval && getuid()) {
345         printf("Enter old secret pass phrase: ");
346         if (!opiereadpass(passwd, sizeof(passwd), 0)) {
347           fprintf(stderr, "Error reading secret pass phrase!\n");
348           finish(NULL);
349         }
350         if (!passwd[0]) {
351           fprintf(stderr, "Secret pass phrase unchanged.\n");
352           finish(NULL);
353         }
354         {
355           struct opie_otpkey key;
356           char tbuf[OPIE_RESPONSE_MAX + 1];
357           
358           if (opiekeycrunch(MDX, &key, opie.opie_seed, passwd) != 0) {
359             fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]);
360             finish(NULL);
361           }
362           memset(passwd, 0, sizeof(passwd));
363           i = opie.opie_n - 1;
364           while (i-- != 0)
365             opiehash(&key, MDX);
366           opiebtoe(tbuf, &key);
367           if (opieverify(&opie, tbuf)) {
368             fprintf(stderr, "Sorry.\n");
369             finish(NULL);
370           }
371         }
372       }
373       for (i = 0;; i++) {
374         if (i > 2)
375           finish(NULL);
376         printf("Enter new secret pass phrase: ");
377         if (!opiereadpass(passwd, sizeof(passwd), 0)) {
378           fprintf(stderr, "Error reading secret pass phrase.\n");
379           finish(NULL);
380         }
381         if (!passwd[0] || feof(stdin)) {
382           fprintf(stderr, "Secret pass phrase unchanged.\n");
383           finish(NULL);
384         }
385         if (opiepasscheck(passwd)) { 
386           memset(passwd, 0, sizeof(passwd));
387           fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
388           continue;
389         }
390         printf("Again new secret pass phrase: ");
391         if (!opiereadpass(passwd2, sizeof(passwd2), 0)) {
392           fprintf(stderr, "Error reading secret pass phrase.\n");
393           finish(NULL);
394         }
395         if (feof(stdin)) {
396           fprintf(stderr, "Secret pass phrase unchanged.\n");
397           finish(NULL);
398         }
399         if (!passwd[0] || !strcmp(passwd, passwd2))
400           break;
401         fprintf(stderr, "Sorry, no match.\n");
402       }
403       memset(passwd2, 0, sizeof(passwd2));
404       if (opiepasswd(&opie, 1 | force, pp->pw_name, n, seed, passwd)) {
405         fprintf(stderr, "Error updating key database.\n");
406         finish(NULL);
407       }
408       finish(pp->pw_name);
409     }
410   case MODE_DISABLE:
411     {
412       char tmp[4];
413       int i;
414
415       for (i = 0;; i++) {
416         if (i > 2)
417           finish(NULL);
418         
419         printf("Disable %s's OTP access? (yes or no) ", pp->pw_name);
420         if (!opiereadpass(tmp, sizeof(tmp), 1)) {
421           fprintf(stderr, "Error reading entry.\n");
422           finish(NULL);
423         }
424         if (!strcmp(tmp, "no"))
425           finish(NULL);
426         if (!strcmp(tmp, "yes")) {
427           if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) {
428             fprintf(stderr, "Error updating key database.\n");
429             finish(NULL);
430           }
431           finish(pp->pw_name);
432         }
433       }
434     }
435   }
436 }