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