installer - Several improvements
[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   char *username;
122   uid_t ruid;
123   struct passwd *pp;
124
125   memset(seed, 0, sizeof(seed));
126
127   ruid = getuid();
128   username = getlogin();
129   pp = getpwnam(username);
130   if (username == NULL || pp == NULL || pp->pw_uid != ruid)
131     pp = getpwuid(ruid);
132   if (pp == NULL) {
133     fprintf(stderr, "Who are you?");
134     return 1;
135   }
136
137   while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) {
138     switch (i) {
139     case 'v':
140       opieversion();
141     case 'f':
142 #if INSECURE_OVERRIDE
143       force = OPIEPASSWD_FORCE;
144 #else /* INSECURE_OVERRIDE */
145       fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
146 #endif /* INSECURE_OVERRIDE */
147       break;
148     case 'c':
149       mode = MODE_CONSOLE;
150       break;
151     case 'd':
152       mode = MODE_DISABLE;
153       break;
154     case 'n':
155       i = atoi(optarg);
156       if (!(i > 0 && i < 10000)) {
157         printf("Sequence numbers must be > 0 and < 10000\n");
158         finish(NULL);
159       }
160       n = i;
161       break;
162     case 's':
163       i = strlen(optarg);
164       if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) {
165         printf("Seeds must be between %d and %d characters long.\n",
166                OPIE_SEED_MIN, OPIE_SEED_MAX);
167         finish(NULL);
168       }
169       opiestrncpy(seed, optarg, sizeof(seed));
170       break;
171     default:
172       usage(argv[0]);
173     }
174   }
175
176   if (argc - optind >= 1) {
177     if (strcmp(argv[optind], pp->pw_name)) {
178       if (getuid()) {
179         printf("Only root can change others' passwords.\n");
180         exit(1);
181       }
182       if ((pp = getpwnam(argv[optind])) == NULL) {
183         printf("%s: user unknown.\n", argv[optind]);
184         exit(1);
185       }
186     }
187   }
188
189   opielock(pp->pw_name);
190   rval = opielookup(&opie, pp->pw_name);
191
192   switch (rval) {
193   case 0:
194     printf("Updating %s:\n", pp->pw_name);
195     break;
196   case 1:
197     printf("Adding %s:\n", pp->pw_name);
198     break;
199   case 2:
200     fprintf(stderr, "Error: Can't update key database.\n");
201     finish(NULL);
202   default:
203     fprintf(stderr, "Error reading key database\n");
204     finish(NULL);
205   }
206
207   if (seed[0]) {
208     i = strlen(seed);
209     if (i > OPIE_SEED_MAX) {
210       fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX);
211       finish(NULL);
212     }
213     if (i < OPIE_SEED_MIN) {
214       fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN);
215       finish(NULL);
216     }
217   } else {
218     if (!rval)
219       strcpy(seed, opie.opie_seed);
220
221     if (opienewseed(seed) < 0) {
222       fprintf(stderr, "Error updating seed.\n");
223       finish(NULL);
224     }
225   }
226
227   if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) {
228     fprintf(stderr, "You must use a different seed for the new OTP sequence.\n");
229     finish(NULL);
230   }
231   
232   switch(mode) {
233   case MODE_DEFAULT:
234     {
235       char tmp[OPIE_RESPONSE_MAX + 2];
236       
237       printf("You need the response from an OTP generator.\n");
238 #if DEBUG
239       if (!rval) {
240 #else /* DEBUG */
241       if (!rval && getuid()) {
242 #endif /* DEBUG */
243         char oseed[OPIE_SEED_MAX + 1];
244         int on;
245
246         if (opiechallenge(&opie, pp->pw_name, tmp)) {
247           fprintf(stderr, "Error issuing challenge.\n");
248           finish(NULL);
249         }
250         on = opiegetsequence(&opie);
251         {
252           char *c;
253           if ((c = strrchr(tmp, ' ')) != NULL)
254             opiestrncpy(oseed, c + 1, sizeof(oseed));
255           else {
256 #if DEBUG
257             fprintf(stderr, "opiepasswd: bogus challenge\n");
258 #endif /* DEBUG */
259             finish(NULL);
260           }
261         }
262         printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp);
263         if (!opiereadpass(tmp, sizeof(tmp), 1))
264           tmp[0] = 0;
265         i = opieverify(&opie, tmp);
266         if (!tmp[0]) {
267           fprintf(stderr, "Error reading response.\n");
268           finish(NULL);
269         }
270         if (i) {
271           fprintf(stderr, "Error verifying response.\n");
272 #if DEBUG
273           fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i);
274 #endif /* DEBUG */
275           finish(NULL);
276         }
277         {
278           char nseed[OPIE_SEED_MAX + 1];
279           int nn;
280
281           if (opiechallenge(&opie, pp->pw_name, tmp)) {
282             fprintf(stderr, "Error verifying database.\n");
283             finish(NULL);
284           }
285
286           nn = opiegetsequence(&opie);
287           {
288             char *c;
289             if ((c = strrchr(tmp, ' ')) != NULL)
290               opiestrncpy(nseed, c + 1, sizeof(nseed));
291             else {
292 #if DEBUG
293               fprintf(stderr, "opiepasswd: bogus challenge\n");
294 #endif /* DEBUG */
295               finish(NULL);
296             }
297           }
298
299           opieverify(&opie, "");
300           nn++;
301
302           if ((nn != on) || strcmp(oseed, nseed))
303             finish(pp->pw_name);
304         }
305       }
306       printf("New secret pass phrase:");
307       for (i = 0;; i++) {
308         if (i > 2)
309           finish(NULL);
310         printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed);
311         if (!opiereadpass(tmp, sizeof(tmp), 1)) {
312           fprintf(stderr, "Error reading response.\n");
313           finish(NULL);
314         }
315         if (tmp[0] == '?') {
316           printf("Enter the response from your OTP calculator: \n");
317           continue;
318         }
319         if (tmp[0] == '\0') {
320           fprintf(stderr, "Secret pass phrase unchanged.\n");
321           finish(NULL);
322         }
323         
324         if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp)))
325           finish(pp->pw_name);
326         
327         if (rval < 0) {
328           fprintf(stderr, "Error updating key database.\n");
329           finish(NULL);
330         }
331         printf("\tThat is not a valid OTP response.\n");
332       }
333     }
334     break;
335   case MODE_CONSOLE:
336     {
337       char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1];
338       /* Get user's secret password */
339       fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n");
340       fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n");
341       fprintf(stderr, "Then run opiepasswd without the -c parameter.\n");
342       if (opieinsecure() && !force) {
343         fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
344         if (force)
345           fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
346         else
347           finish(NULL);
348       };
349       printf("Using %s to compute responses.\n", algnames[MDX]);
350       if (!rval && getuid()) {
351         printf("Enter old secret pass phrase: ");
352         if (!opiereadpass(passwd, sizeof(passwd), 0)) {
353           fprintf(stderr, "Error reading secret pass phrase!\n");
354           finish(NULL);
355         }
356         if (!passwd[0]) {
357           fprintf(stderr, "Secret pass phrase unchanged.\n");
358           finish(NULL);
359         }
360         {
361           struct opie_otpkey key;
362           char tbuf[OPIE_RESPONSE_MAX + 1];
363           
364           if (opiekeycrunch(MDX, &key, opie.opie_seed, passwd) != 0) {
365             fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]);
366             finish(NULL);
367           }
368           memset(passwd, 0, sizeof(passwd));
369           i = opie.opie_n - 1;
370           while (i-- != 0)
371             opiehash(&key, MDX);
372           opiebtoe(tbuf, &key);
373           if (opieverify(&opie, tbuf)) {
374             fprintf(stderr, "Sorry.\n");
375             finish(NULL);
376           }
377         }
378       }
379       for (i = 0;; i++) {
380         if (i > 2)
381           finish(NULL);
382         printf("Enter new secret pass phrase: ");
383         if (!opiereadpass(passwd, sizeof(passwd), 0)) {
384           fprintf(stderr, "Error reading secret pass phrase.\n");
385           finish(NULL);
386         }
387         if (!passwd[0] || feof(stdin)) {
388           fprintf(stderr, "Secret pass phrase unchanged.\n");
389           finish(NULL);
390         }
391         if (opiepasscheck(passwd)) { 
392           memset(passwd, 0, sizeof(passwd));
393           fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
394           continue;
395         }
396         printf("Again new secret pass phrase: ");
397         if (!opiereadpass(passwd2, sizeof(passwd2), 0)) {
398           fprintf(stderr, "Error reading secret pass phrase.\n");
399           finish(NULL);
400         }
401         if (feof(stdin)) {
402           fprintf(stderr, "Secret pass phrase unchanged.\n");
403           finish(NULL);
404         }
405         if (!passwd[0] || !strcmp(passwd, passwd2))
406           break;
407         fprintf(stderr, "Sorry, no match.\n");
408       }
409       memset(passwd2, 0, sizeof(passwd2));
410       if (opiepasswd(&opie, 1 | force, pp->pw_name, n, seed, passwd)) {
411         fprintf(stderr, "Error updating key database.\n");
412         finish(NULL);
413       }
414       finish(pp->pw_name);
415     }
416   case MODE_DISABLE:
417     {
418       char tmp[4];
419       int i;
420
421       for (i = 0;; i++) {
422         if (i > 2)
423           finish(NULL);
424         
425         printf("Disable %s's OTP access? (yes or no) ", pp->pw_name);
426         if (!opiereadpass(tmp, sizeof(tmp), 1)) {
427           fprintf(stderr, "Error reading entry.\n");
428           finish(NULL);
429         }
430         if (!strcmp(tmp, "no"))
431           finish(NULL);
432         if (!strcmp(tmp, "yes")) {
433           if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) {
434             fprintf(stderr, "Error updating key database.\n");
435             finish(NULL);
436           }
437           finish(pp->pw_name);
438         }
439       }
440     }
441   }
442 }