* Fix a bug in runcmd() - the argv[] list was not NULL terminated.
[dragonfly.git] / contrib / opie / opiepasswd.c
blobef78bb3ff2b1b97242fa8cdee3644f27ba34df59
1 /* opiepasswd.c: Add/change an OTP password in the key database.
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>.
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.
15 History:
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).
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 $
50 #include "opie_cfg.h"
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 */
68 #include "opie.h"
70 #define MODE_DEFAULT 0
71 #define MODE_CONSOLE 1
72 #define MODE_DISABLE 2
74 extern int optind;
75 extern char *optarg;
77 char *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" };
78 char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };
80 static VOIDRET usage FUNCTION((myname), char *myname)
82 fprintf(stderr, "usage: %s [-v] [-h] [-c|-d] [-f] [-n initial_sequence_number]\n [-s seed] [username]\n", myname);
83 exit(1);
86 static VOIDRET finish FUNCTION((name), char *name)
88 struct opie opie;
89 char buf[OPIE_RESPONSE_MAX + 1];
91 if (name) {
92 if (opiechallenge(&opie, name, buf)) {
93 fprintf(stderr, "Error verifying database.\n");
94 finish(NULL);
96 printf("\nID %s ", opie.opie_principal);
97 if (opie.opie_val && (opie.opie_val[0] == '*')) {
98 printf("is disabled.\n");
99 finish(NULL);
101 printf("OTP key is %d %s\n", opie.opie_n, opie.opie_seed);
103 struct opie_otpkey key;
105 if (!opieatob8(&key, opie.opie_val)) {
106 fprintf(stderr, "Error verifying key -- possible database corruption.\n");
107 finish(NULL);
109 printf("%s\n", opiebtoe(buf, &key));
113 while(!opieunlock());
114 exit(name ? 0 : 1);
117 int main FUNCTION((argc, argv), int argc AND char *argv[])
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;
126 memset(seed, 0, sizeof(seed));
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;
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);
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);
170 opiestrncpy(seed, optarg, sizeof(seed));
171 break;
172 default:
173 usage(argv[0]);
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);
183 if ((pp = getpwnam(argv[optind])) == NULL) {
184 printf("%s: user unknown.\n", argv[optind]);
185 exit(1);
190 opielock(pp->pw_name);
191 rval = opielookup(&opie, pp->pw_name);
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);
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);
214 if (i < OPIE_SEED_MIN) {
215 fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN);
216 finish(NULL);
218 } else {
219 if (!rval)
220 strcpy(seed, opie.opie_seed);
222 if (opienewseed(seed) < 0) {
223 fprintf(stderr, "Error updating seed.\n");
224 finish(NULL);
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);
233 switch(mode) {
234 case MODE_DEFAULT:
236 char tmp[OPIE_RESPONSE_MAX + 2];
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;
247 if (opiechallenge(&opie, pp->pw_name, tmp)) {
248 fprintf(stderr, "Error issuing challenge.\n");
249 finish(NULL);
251 on = opiegetsequence(&opie);
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);
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);
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);
279 char nseed[OPIE_SEED_MAX + 1];
280 int nn;
282 if (opiechallenge(&opie, pp->pw_name, tmp)) {
283 fprintf(stderr, "Error verifying database.\n");
284 finish(NULL);
287 nn = opiegetsequence(&opie);
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);
300 opieverify(&opie, "");
301 nn++;
303 if ((nn != on) || strcmp(oseed, nseed))
304 finish(pp->pw_name);
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);
316 if (tmp[0] == '?') {
317 printf("Enter the response from your OTP calculator: \n");
318 continue;
320 if (tmp[0] == '\0') {
321 fprintf(stderr, "Secret pass phrase unchanged.\n");
322 finish(NULL);
325 if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp)))
326 finish(pp->pw_name);
328 if (rval < 0) {
329 fprintf(stderr, "Error updating key database.\n");
330 finish(NULL);
332 printf("\tThat is not a valid OTP response.\n");
335 break;
336 case MODE_CONSOLE:
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);
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);
357 if (!passwd[0]) {
358 fprintf(stderr, "Secret pass phrase unchanged.\n");
359 finish(NULL);
362 struct opie_otpkey key;
363 char tbuf[OPIE_RESPONSE_MAX + 1];
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);
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);
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);
388 if (!passwd[0] || feof(stdin)) {
389 fprintf(stderr, "Secret pass phrase unchanged.\n");
390 finish(NULL);
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;
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);
402 if (feof(stdin)) {
403 fprintf(stderr, "Secret pass phrase unchanged.\n");
404 finish(NULL);
406 if (!passwd[0] || !strcmp(passwd, passwd2))
407 break;
408 fprintf(stderr, "Sorry, no match.\n");
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);
415 finish(pp->pw_name);
417 case MODE_DISABLE:
419 char tmp[4];
420 int i;
422 for (i = 0;; i++) {
423 if (i > 2)
424 finish(NULL);
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);
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);
438 finish(pp->pw_name);