xref: /freebsd-13-stable/contrib/opie/opiepasswd.c (revision d16f6f50276f22d892749e99b9ad5f6b7e1d41c8)
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$
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, ' '))
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, ' '))
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 }
443