summaryrefslogtreecommitdiffstats
path: root/contrib/opie/opiepasswd.c
blob: 6cb272898e83df7d647db7a05fb555764e8e15ac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
/* opiepasswd.c: Add/change an OTP password in the key database.

%%% portions-copyright-cmetz-96
Portions of this software are Copyright 1996-1998 by Craig Metz, All Rights
Reserved. The Inner Net License Version 2 applies to these portions of
the software.
You should have received a copy of the license with this software. If
you didn't get a copy, you may request one from <license@inner.net>.

Portions of this software are Copyright 1995 by Randall Atkinson and Dan
McDonald, All Rights Reserved. All Rights under this copyright are assigned
to the U.S. Naval Research Laboratory (NRL). The NRL Copyright Notice and
License Agreement applies to this software.

	History:

	Modified by cmetz for OPIE 2.32. Use OPIE_SEED_MAX instead of
		hard coding the length. Unlock user on failed lookup.
	Modified by cmetz for OPIE 2.3. Got of some variables and made some
		local to where they're used. Split out the finishing code. Use
		opielookup() instead of opiechallenge() to find user. Three
		strikes on prompts. Use opiepasswd()'s new calling
		convention. Changed OPIE_PASS_{MAX,MIN} to
		OPIE_SECRET_{MAX,MIN}. Handle automatic reinits happenning
		below us. Got rid of unneeded headers. Use new opieatob8()
		return value convention. Added -f flag. Added SHA support.
	Modified by cmetz for OPIE 2.22. Finally got rid of the lock
	        filename kluge by implementing refcounts for locks.
		Use opiepasswd() to update key file. Error if we can't
		write to the key file. Check for minimum seed length.
        Modified at NRL for OPIE 2.2. Changed opiestrip_crlf to
                opiestripcrlf. Check opiereadpass() return value.
                Minor optimization. Change calls to opiereadpass() to
                use echo arg. Use opiereadpass() where we can.
                Make everything static. Ifdef around some headers.
                Changed use of gethostname() to uname(). Got rid of
                the need for buf[]. Properly check return value of
                opieatob8. Check seed length. Always generate proper-
                length seeds.
	Modified at NRL for OPIE 2.1. Minor autoconf changes.
        Modified heavily at NRL for OPIE 2.0.
	Written at Bellcore for the S/Key Version 1 software distribution
		(skeyinit.c).

 $FreeBSD$
*/
#include "opie_cfg.h"

#if HAVE_PWD_H
#include <pwd.h>
#endif /* HAVE_PWD_H */
#include <stdio.h>
#if HAVE_STRING_H
#include <string.h>
#endif /* HAVE_STRING_H */
#include <stdio.h>
#include <sys/types.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */
#if HAVE_STDLIB_H
#include <stdlib.h>
#endif /* HAVE_STDLIB_H */

#include "opie.h"

#define MODE_DEFAULT 0
#define MODE_CONSOLE 1
#define MODE_DISABLE 2

extern int optind;
extern char *optarg;

char *algnames[] = { NULL, NULL, NULL, "SHA-1", "MD4", "MD5" };
char *algids[] = { NULL, NULL, NULL, "sha1", "md4", "md5" };

static VOIDRET usage FUNCTION((myname), char *myname)
{
  fprintf(stderr, "usage: %s [-v] [-h] [-c|-d] [-f] [-n initial_sequence_number]\n                            [-s seed] [username]\n", myname);
  exit(1);
}

static VOIDRET finish FUNCTION((name), char *name)
{
  struct opie opie;
  char buf[OPIE_RESPONSE_MAX + 1];

  if (name) {
    if (opiechallenge(&opie, name, buf)) {
      fprintf(stderr, "Error verifying database.\n");
      finish(NULL);
    }
    printf("\nID %s ", opie.opie_principal);
    if (opie.opie_val && (opie.opie_val[0] == '*')) {
      printf("is disabled.\n");
      finish(NULL);
    }
    printf("OTP key is %d %s\n", opie.opie_n, opie.opie_seed);
    {
      char key[8];
      if (!opieatob8(key, opie.opie_val)) {
	fprintf(stderr, "Error verifying key -- possible database corruption.\n");
	finish(NULL);
      }
      printf("%s\n", opiebtoe(buf, key));
    }
  }

  while(!opieunlock());
  exit(name ? 0 : 1);
}

int main FUNCTION((argc, argv), int argc AND char *argv[])
{
  struct opie opie;
  int rval, n = 499, i, mode = MODE_DEFAULT, force = 0;
  char seed[OPIE_SEED_MAX+1];
  struct passwd *pp;

  memset(seed, 0, sizeof(seed));

  if (!(pp = getpwnam(getlogin()))) {
    fprintf(stderr, "Who are you?");
    return 1;
  }

  while ((i = getopt(argc, argv, "fhvcn:s:d")) != EOF) {
    switch (i) {
    case 'v':
      opieversion();
    case 'f':
#if INSECURE_OVERRIDE
      force = OPIEPASSWD_FORCE;
#else /* INSECURE_OVERRIDE */
      fprintf(stderr, "Sorry, but the -f option is not supported by this build of OPIE.\n");
#endif /* INSECURE_OVERRIDE */
      break;
    case 'c':
      mode = MODE_CONSOLE;
      break;
    case 'd':
      mode = MODE_DISABLE;
      break;
    case 'n':
      i = atoi(optarg);
      if (!(i > 0 && i < 10000)) {
	printf("Sequence numbers must be > 0 and < 10000\n");
	finish(NULL);
      }
      n = i;
      break;
    case 's':
      i = strlen(optarg);
      if ((i > OPIE_SEED_MAX) || (i < OPIE_SEED_MIN)) {
	printf("Seeds must be between %d and %d characters long.\n",
	       OPIE_SEED_MIN, OPIE_SEED_MAX);
	finish(NULL);
      }
      strncpy(seed, optarg, sizeof(seed));
      seed[sizeof(seed) - 1] = 0;
      break;
    default:
      usage(argv[0]);
    }
  }

  if (argc - optind >= 1) {
    if (strcmp(argv[optind], pp->pw_name)) {
      if (getuid()) {
	printf("Only root can change others' passwords.\n");
	exit(1);
      }
      if ((pp = getpwnam(argv[optind])) == NULL) {
	printf("%s: user unknown.\n", argv[optind]);
	exit(1);
      }
    }
  }

  opielock(pp->pw_name);
  rval = opielookup(&opie, pp->pw_name);

  switch (rval) {
  case 0:
    printf("Updating %s:\n", pp->pw_name);
    break;
  case 1:
    printf("Adding %s:\n", pp->pw_name);
    break;
  case 2:
    fprintf(stderr, "Error: Can't update key database.\n");
    finish(NULL);
  default:
    fprintf(stderr, "Error reading key database\n");
    finish(NULL);
  }

  if (seed[0]) {
    i = strlen(seed);
    if (i > OPIE_SEED_MAX) {
      fprintf(stderr, "Seeds must be less than %d characters long.", OPIE_SEED_MAX);
      finish(NULL);
    }
    if (i < OPIE_SEED_MIN) {
      fprintf(stderr, "Seeds must be greater than %d characters long.", OPIE_SEED_MIN);
      finish(NULL);
    }
  } else {
    if (!rval)
      strcpy(seed, opie.opie_seed);

    if (opienewseed(seed) < 0) {
      fprintf(stderr, "Error updating seed.\n");
      finish(NULL);
    }
  }

  if (opie.opie_seed && opie.opie_seed[0] && !strcmp(opie.opie_seed, seed)) {
    fprintf(stderr, "You must use a different seed for the new OTP sequence.\n");
    finish(NULL);
  }
  
  switch(mode) {
  case MODE_DEFAULT:
    {
      char tmp[OPIE_RESPONSE_MAX + 2];
      
      printf("You need the response from an OTP generator.\n");
#if DEBUG
      if (!rval) {
#else /* DEBUG */
      if (!rval && getuid()) {
#endif /* DEBUG */
	char oseed[OPIE_SEED_MAX + 1];
	int on;

	if (opiechallenge(&opie, pp->pw_name, tmp)) {
	  fprintf(stderr, "Error issuing challenge.\n");
	  finish(NULL);
	}
	on = opiegetsequence(&opie);
	{
	  char *c;
	  if (c = strrchr(tmp, ' '))
	    strncpy(oseed, c + 1, sizeof(oseed));
	  else {
#if DEBUG
	    fprintf(stderr, "opiepasswd: bogus challenge\n");
#endif /* DEBUG */
	    finish(NULL);
	  }
	}
	printf("Old secret pass phrase:\n\t%s\n\tResponse: ", tmp);
	if (!opiereadpass(tmp, sizeof(tmp), 1))
	  tmp[0] = 0;
	i = opieverify(&opie, tmp);
	if (!tmp[0]) {
	  fprintf(stderr, "Error reading response.\n");
	  finish(NULL);
	}
	if (i) {
	  fprintf(stderr, "Error verifying response.\n");
#if DEBUG
	  fprintf(stderr, "opiepasswd: opieverify() returned %d\n", i);
#endif /* DEBUG */
	  finish(NULL);
	}
	{
	  char nseed[OPIE_SEED_MAX + 1];
	  int nn;

	  if (opiechallenge(&opie, pp->pw_name, tmp)) {
	    fprintf(stderr, "Error verifying database.\n");
	    finish(NULL);
	  }

	  nn = opiegetsequence(&opie);
	  {
	    char *c;
	    if (c = strrchr(tmp, ' '))
	      strncpy(nseed, c + 1, sizeof(nseed));
	    else {
#if DEBUG
	      fprintf(stderr, "opiepasswd: bogus challenge\n");
#endif /* DEBUG */
	      finish(NULL);
	    }
	  }

	  opieverify(&opie, "");
	  nn++;

	  if ((nn != on) || strcmp(oseed, nseed))
	    finish(pp->pw_name);
	}
      }
      printf("New secret pass phrase:");
      for (i = 0;; i++) {
	if (i > 2)
	  finish(NULL);
	printf("\n\totp-%s %d %s\n\tResponse: ", algids[MDX], n, seed);
	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
	  fprintf(stderr, "Error reading response.\n");
	  finish(NULL);
	}
	if (tmp[0] == '?') {
	  printf("Enter the response from your OTP calculator: \n");
	  continue;
	}
	if (tmp[0] == '\0') {
	  fprintf(stderr, "Secret pass phrase unchanged.\n");
	  finish(NULL);
	}
	
	if (!(rval = opiepasswd(&opie, force, pp->pw_name, n, seed, tmp)))
	  finish(pp->pw_name);
	
	if (rval < 0) {
	  fprintf(stderr, "Error updating key database.\n");
	  finish(NULL);
	}
	printf("\tThat is not a valid OTP response.\n");
      }
    }
    break;
  case MODE_CONSOLE:
    {
      char passwd[OPIE_SECRET_MAX + 1], passwd2[OPIE_SECRET_MAX + 1];
      /* Get user's secret password */
      fprintf(stderr, "Only use this method from the console; NEVER from remote. If you are using\n");
      fprintf(stderr, "telnet, xterm, or a dial-in, type ^C now or exit with no password.\n");
      fprintf(stderr, "Then run opiepasswd without the -c parameter.\n");
      if (opieinsecure() && !force) {
	fprintf(stderr, "Sorry, but you don't seem to be on the console or a secure terminal.\n");
	if (force)
          fprintf(stderr, "Warning: Continuing could disclose your secret pass phrase to an attacker!\n");
        else
          finish(NULL);
      };
      printf("Using %s to compute responses.\n", algnames[MDX]);
      if (!rval && getuid()) {
	printf("Enter old secret pass phrase: ");
	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
	  fprintf(stderr, "Error reading secret pass phrase!\n");
	  finish(NULL);
	}
	if (!passwd[0]) {
	  fprintf(stderr, "Secret pass phrase unchanged.\n");
	  finish(NULL);
	}
	{
	  char key[8];
	  char tbuf[OPIE_RESPONSE_MAX + 1];
	  
	  if (opiekeycrunch(MDX, key, opie.opie_seed, passwd) != 0) {
	    fprintf(stderr, "%s: key crunch failed. Secret pass phrase unchanged\n", argv[0]);
	    finish(NULL);
	  }
	  memset(passwd, 0, sizeof(passwd));
	  i = opie.opie_n - 1;
	  while (i-- != 0)
	    opiehash(key, MDX);
	  opiebtoe(tbuf, key);
	  if (opieverify(&opie, tbuf)) {
	    fprintf(stderr, "Sorry.\n");
	    finish(NULL);
	  }
	}
      }
      for (i = 0;; i++) {
	if (i > 2)
	  finish(NULL);
	printf("Enter new secret pass phrase: ");
	if (!opiereadpass(passwd, sizeof(passwd), 0)) {
	  fprintf(stderr, "Error reading secret pass phrase.\n");
	  finish(NULL);
	}
	if (!passwd[0] || feof(stdin)) {
	  fprintf(stderr, "Secret pass phrase unchanged.\n");
	  finish(NULL);
	}
	if (opiepasscheck(passwd)) { 
	  memset(passwd, 0, sizeof(passwd));
	  fprintf(stderr, "Secret pass phrases must be between %d and %d characters long.\n", OPIE_SECRET_MIN, OPIE_SECRET_MAX);
	  continue;
	}
	printf("Again new secret pass phrase: ");
	if (!opiereadpass(passwd2, sizeof(passwd2), 0)) {
	  fprintf(stderr, "Error reading secret pass phrase.\n");
	  finish(NULL);
	}
	if (feof(stdin)) {
	  fprintf(stderr, "Secret pass phrase unchanged.\n");
	  finish(NULL);
	}
	if (!passwd[0] || !strcmp(passwd, passwd2))
	  break;
	fprintf(stderr, "Sorry, no match.\n");
      }
      memset(passwd2, 0, sizeof(passwd2));
      if (opiepasswd(&opie, 1 | force, pp->pw_name, n, seed, passwd)) {
	fprintf(stderr, "Error updating key database.\n");
	finish(NULL);
      }
      finish(pp->pw_name);
    }
  case MODE_DISABLE:
    {
      char tmp[4];
      int i;

      for (i = 0;; i++) {
	if (i > 2)
	  finish(NULL);
	
	printf("Disable %s's OTP access? (yes or no) ", pp->pw_name);
	if (!opiereadpass(tmp, sizeof(tmp), 1)) {
	  fprintf(stderr, "Error reading entry.\n");
	  finish(NULL);
	}
	if (!strcmp(tmp, "no"))
	  finish(NULL);
	if (!strcmp(tmp, "yes")) {
	  if (opiepasswd(&opie, 0, pp->pw_name, n, seed, NULL)) {
	    fprintf(stderr, "Error updating key database.\n");
	    finish(NULL);
	  }
	  finish(pp->pw_name);
	}
      }
    }
  }
}
OpenPOWER on IntegriCloud