1 /* popauth.c - manipulate POP authentication DB:
2 * aka "apopauth" [same thing really] and "scramauth" [manipulate
3 * scram entries]
4 */
5
6
7 /*
8 * Copyright (c) 2011 QUALCOMM Incorporated. All rights reserved.
9 * The file License.txt specifies the terms for use, modification,
10 * and redistribution.
11 *
12 *
13 * Revisions:
14 *
15 * 06/21/08 [Randall Gellens]
16 * - Use new "uint4.h" for UINT4 typedef
17 *
18 * 4/11/05 [Randall Gellens]
19 * - Open trace file as user, not root (fix security hole
20 * reported by Jens Steube)
21 * - Set umask to make sure we don't create group- or world-
22 * writable files (e.g., trace or db)
23 *
24 * 7/29/03 [Randall Gellens]
25 * - Removed prototype for srandom(); use stdlib.h instead.
26 *
27 * 1/02/02 [Pierre Humblet]
28 * - Added Cygwin specific code
29 *
30 * 01/02/00 [Randall Gellens]
31 * - Added checks for dbm errors.
32 *
33 * 12/29/00 [Randall Gellens]
34 * - Added -safe flag for -init.
35 *
36 * 8/20/00 [Randall Gellens]
37 * - Added logging and tracing.
38 * - Reorganized command-line syntax.
39 * - Users can now list their own entry. '-list ALL' lists
40 * all users.
41 *
42 * 6/30/00 [Randall Gellens]
43 * - Ensured all uses of strncpy properly null-terminate; use
44 * strlcpy for many.
45 *
46 * 6/14/00 [Randall Gellens]
47 * - Fixed passed-in password so it doesn't prompt to confirm.
48 * - Added -help switch.
49 *
50 * 6/6/00 [Randall Gellens]
51 * - Allow password to be passed as extra parameter to popauth
52 * so it can be used in a scripted envirovment (based on patch
53 * by Dejan Ilic).
54 * - Cast value.dptr to char * when dereferencing to avoid errors
55 * on IRIX, using patch from Rick Troxel.
56 *
57 * 4/21/00 [Randall Gellens]
58 * - Remove __STDC__ (some compilers don't define this when extensions
59 * are enabled, leading to the erroneous conclusion that ANSI C is
60 * not supported. These days, it is far more likely that a compiler
61 * fails to correctly handle K&R syntax than ANSI.)
62 *
63 * 4/29/99 [Randall Gellens]
64 * - Don't reference auth_file when GDBM is undefined (syntax error)
65 *
66 * 2/04/99 [Randall Gellens]
67 * - Don't flock() when using gdbm.
68 *
69 * 2/13/98 [Rob Duwors]
70 * - Added SCRAM support.
71 */
72
73 /*
74 *. All fields for a given user entry are null terminated strings.
75 *. APOP field come first [may be a null string if logically absent].
76 *. SCRAM field come second [may be physically or logically absent].
77 *. SCRAM fields are kept in base64 representation of the original 40 byte
78 * SCRAM verifier (salt, client id, and server key).
79 *. Deleting a user still deletes the entire entry [APOP and SCRAM
80 * disappear].
81 *. parameter -list also shows the presence of APOP and SCRAM entries.
82 *. old authentication databases and APOP password fields can be used
83 * as is [the missing SCRAM field will be treated as null]
84 *. a specific field [APOP or SCRAM] can be cleared to null by using
85 * password "*", i.e. a single star character as the new password.
86 *. scramauth by default will clear the APOP field [disabling APOP
87 * access for that user]. This can be stopped by using the -only
88 * switch.
89 *. popauth/apopauth can ONLY change the APOP password, never distrubing
90 * the the SCRAM field.
91 *. APOP password and SCRAM pass phrases are not related to each other
92 * in any way ([a]pop|scram)auth, i.e. the APOP password can not
93 * compromise the SCRAM verifiers, except by user choice.
94 */
95
96 #include "config.h"
97 #include "uint4.h"
98
99 #ifdef SCRAM
100 # include <md5.h>
101 # include <hmac-md5.h>
102 # include <scram.h>
103 #endif /* SCRAM */
104
105 #ifdef HAVE_GDBM_H
106 # include <gdbm.h>
107 #else /* no GDBM */
108 # ifdef HAVE_NDBM_H
109 # include <ndbm.h>
110 # else /* no NDBM */
111 # ifdef HAVE_DBM_H
112 # include <dbm.h>
113 # endif /* HAVE_DBM_H */
114 # endif /* HAVE_NDBM_H */
115 #endif /* HAVE_GDBM_H */
116
117 #include <sys/types.h>
118 #include <pwd.h>
119 #include <stdio.h>
120 #include <stdlib.h>
121 #include <stdarg.h>
122 #include <sys/stat.h>
123 #include <fcntl.h>
124 #include <errno.h>
125 #include <string.h>
126
127 #ifndef HAVE_BCOPY
128 # define bcopy(src,dest,len) (void) (memcpy(dest,src,len))
129 # define bzero(dest,len) (void) (memset(dest, (char)NULL, len))
130 # define bcmp(b1,b2,n) memcmp(b1,b2,n)
131 #endif /* HAVE_BCOPY */
132
133 #ifndef HAVE_INDEX
134 # define index(s,c) strchr(s,c)
135 # define rindex(s,c) strrchr(s,c)
136 #endif /* HAVE_INDEX */
137
138 /*
139 * Some old OSes don't have srandom, but do have srand
140 */
141 #ifndef HAVE_SRANDOM
142 # include <stdlib.h>
143 # define srandom srand
144 # define random rand
145 #elif defined(__CYGWIN__)
146 # include <stdlib.h>
147 #endif /* HAVE_SRANDOM */
148
149 #include <flock.h>
150
151 #if HAVE_STRINGS_H
152 # include <strings.h>
153 #endif /* HAVE_STRINGS_H */
154
155 #if HAVE_SYS_FILE_H
156 # include <sys/file.h>
157 #endif /* HAVE_SYS_FILE_H */
158
159 #include "string_util.h"
160
161 #include <time.h>
162 #define TIME_T time_t
163
164 #ifdef SCRAM
165 extern int encode64();
166 extern int decode64();
167 #endif /* SCRAM */
168
169 #ifndef HAVE_STRERROR
170 char *strerror();
171 #endif /* HAVE_STRERROR */
172
173 #ifdef HAVE_SYS_UNISTD_H
174 # include <sys/unistd.h>
175 #endif /* HAVE_SYS_UNISTD_H */
176
177 #ifdef HAVE_UNISTD_H
178 # include <unistd.h>
179 #endif /* include <unistd.h> */
180
181 #include "popper.h"
182 #include "logit.h"
183
184 #define UID_T uid_t
185
186
187 static struct mods {
188 char *name;
189 } modes[] = {
190 #define SCRAM_AUTH 0
191 { "qscramauth" },
192 #define APOP_AUTH 1
193 { "qapopauth" },
194 #define POP_AUTH 2
195 { "qpopauth" },
196 #define OTHER 3
197 { NULL }
198 };
199
200
201 typedef enum {
202 INITSW = 1,
203 SAFESW,
204 LISTSW,
205 USERSW,
206 DELESW,
207 ONLYSW,
208 HELPSW,
209 TRACESW,
210 DEBUGSW,
211 VERSNSW,
212 DUMPSW,
213
214 BAD_SW
215 } switchType;
216
217
218 static struct swit {
219 char *name;
220 int param;
221 switchType val;
222 } switches[] = {
223 { "init" , 0, INITSW },
224 { "safe" , 0, SAFESW },
225 { "list" , 0, LISTSW },
226 { "user" , 1, USERSW },
227 { "delete" , 1, DELESW },
228 { "only" , 0, ONLYSW },
229 { "help" , 0, HELPSW },
230 { "-help" , 0, HELPSW },
231 { "trace" , 1, TRACESW },
232 { "debug" , 0, DEBUGSW },
233 { "version" , 0, VERSNSW },
234 #ifdef _DEBUG
235 { "dump" , 0, DUMPSW },
236 #endif /* _DEBUG */
237
238 { NULL , 0, BAD_SW },
239 };
240
241
242 #define BLATHER0(b) do { \
243 if ( debug ) \
244 logit ( trace_file, POP_DEBUG, HERE, b); \
245 } while ( 0 );
246 #define BLATHER1(b,a1) do { \
247 if ( debug ) \
248 logit ( trace_file, POP_DEBUG, HERE, b, a1); \
249 } while ( 0 );
250 #define BLATHER2(b,a1,a2) do { \
251 if ( debug ) \
252 logit ( trace_file, POP_DEBUG, HERE, b, a1, a2); \
253 } while ( 0 );
254 #define BLATHER3(b,a1,a2,a3) do { \
255 if ( debug ) \
256 logit ( trace_file, POP_DEBUG, HERE, b, a1, a2, a3); \
257 }while(0);
258 #define BLATHER4(b,a1,a2,a3,a4) do { \
259 if ( debug ) \
260 logit ( trace_file, POP_DEBUG, HERE, b, a1, a2, a3, a4); \
261 } while ( 0 );
262 #define BLATHER5(b,a1,a2,a3,a4,a5) do { \
263 if ( debug ) \
264 logit ( trace_file, POP_DEBUG, HERE, b, a1, a2, a3, a4, a5); \
265 } while ( 0 );
266 #define BLATHER6(b,a1,a2,a3,a4,a5,a6) do { \
267 if ( debug ) \
268 logit ( trace_file, POP_DEBUG, HERE, b, a1, a2, a3, a4, a5, \
269 a6); \
270 } while ( 0 );
271 #define BLATHER7(b,a1,a2,a3,a4,a5,a6,a7) do { \
272 if ( debug ) \
273 logit ( trace_file, POP_DEBUG, HERE, b, a1, a2, a3, a4, a5, \
274 a6, a7); \
275 } while ( 0 );
276 #define BLATHER8(b,a1,a2,a3,a4,a5,a6,a7,a8) do { \
277 if ( debug ) \
278 logit ( trace_file, POP_DEBUG, HERE, b, a1, a2, a3, a4, a5, \
279 a6, a7, a8); \
280 } while ( 0 );
281 #define BLATHER9(b,a1,a2,a3,a4,a5,a6,a7,a8,a9) do { \
282 if ( debug ) \
283 logit ( trace_file, POP_DEBUG, HERE, b, a1, a2, a3, a4, a5, \
284 a6, a7, a8, a9); \
285 } while ( 0 );
286
287
288 /*
289 * Globals
290 */
291 static char *program = NULL;
292 static int debug = 0;
293 static FILE *trace_file = NULL;
294
295
296 /*
297 * Prototypes
298 */
299 static void byebye ( int val );
300 static void adios ( WHENCE, const char *fmt, ... );
301 static void helpful ( void );
302 static int check_db_err ( void *db, const char *op, BOOL bExp );
303 static const char *printable ( const char *p, int len );
304 static void open_trace ( char *fname );
305
306
307 static void
byebye(int val)308 byebye ( int val )
309 {
310 if ( trace_file != NULL )
311 fclose ( trace_file );
312
313 exit ( val );
314 }
315
316
317 static void
adios(WHENCE,const char * fmt,...)318 adios ( WHENCE, const char *fmt, ... )
319 {
320 va_list ap;
321
322 fprintf ( stderr, "%s: ", program );
323 va_start ( ap, fmt );
324 vfprintf ( stderr, fmt, ap );
325 fprintf ( stderr, "\n" );
326 va_end ( ap );
327
328 if ( debug ) {
329 va_start ( ap, fmt );
330 vlogit ( trace_file, POP_DEBUG, fn, ln, fmt, ap );
331 va_end ( ap );
332 }
333
334 byebye ( 1 );
335 }
336
337
338 static void
helpful(void)339 helpful ( void )
340 {
341 fprintf ( stderr, "usage: %s [ -debug | -trace <tracefile> ] [ <action> ]\n"
342 "<action>: -init [ -safe ] \n"
343 " -list [ <user> | ALL ] | -only\n"
344 " -user <user> [ <password> ]\n"
345 " -delete <user>\n"
346 " -help\n"
347 " -version\n",
348 program );
349
350 byebye ( 1 );
351 }
352
353
354 /*
355 * Check for db errors.
356 *
357 * Parameters:
358 * db: result of *dbm_open.
359 * op: text string of operation (e.g., "fetch").
360 * bExp: if an error (such as "item not found" is
361 * expected and not anything to get excited
362 * about.
363 */
364 static int
check_db_err(void * db,const char * op,BOOL bExp)365 check_db_err ( void *db, const char *op, BOOL bExp )
366 {
367 int db_err = 0;
368 char *db_str = NULL;
369 int sv_err = errno;
370 int vNotFound = -1;
371 int vEmptyDb = -1;
372
373
374 #ifdef GDBM
375 (void) db;
376 db_err = (int) gdbm_errno;
377 db_str = (char *) gdbm_strerror ( gdbm_errno );
378 vNotFound = GDBM_ITEM_NOT_FOUND;
379 vEmptyDb = GDBM_EMPTY_DATABASE;
380 #else
381 db_err = dbm_error ( (DBM *) db );
382 db_str = strerror ( db_err );
383 #endif /* GDBM */
384
385 if ( db_err != 0 ) {
386 if ( bExp && ( db_err == vNotFound || db_err == vEmptyDb ) ) {
387 BLATHER5 ( "DB error on %s: %s %d (%d: %s)",
388 op, db_str, db_err, sv_err, strerror(sv_err) );
389 } else {
390 logit ( trace_file, POP_NOTICE, HERE,
391 "*** DB error on %s: %s %d (%d: %s) ***",
392 op, db_str, db_err, sv_err, strerror(sv_err) );
393
394 fprintf ( stderr, "*** DB error on %s: %s %d (%d: %s) ***\n",
395 op, db_str, db_err, sv_err, strerror(sv_err) );
396 }
397 } else {
398 BLATHER5 ( "DB %s succeeded: %s %d (%d: %s)",
399 op, db_str, db_err, sv_err, strerror(sv_err) );
400 }
401
402 return db_err;
403 }
404
405
406 static char printable_buffer [ 1024 ];
407
408 static const char *
printable(const char * p,int len)409 printable ( const char *p, int len )
410 {
411 int left = sizeof ( printable_buffer ) -1;
412 char *q = printable_buffer;
413 char c = 0;
414
415
416 if ( p == NULL ) {
417 BLATHER0 ( "printable passed NULL pointer" );
418 return NULL;
419 }
420
421 /*BLATHER2 ( "printable passed %d (strlen %d)", len, strlen(p) );*/
422
423 while ( left > 0 && len > 0 ) {
424 c = *p++;
425 len--;
426 if ( c >= ' ' && c <= '~' ) {
427 *q++ = c;
428 left -= 1;
429 } else {
430 long ic = c & 0x000000ff;
431 switch ( c ) {
432 case '\0':
433 *q++ = '\\';
434 *q++ = '0';
435 left -= 2;
436 break;
437 case '\n':
438 *q++ = '\\';
439 *q++ = 'n';
440 left -= 2;
441 break;
442 case '\t':
443 *q++ = '\\';
444 *q++ = 't';
445 left -= 2;
446 break;
447 case '\r':
448 *q++ = '\\';
449 *q++ = 'r';
450 left -= 2;
451 break;
452 case '\a':
453 *q++ = '\\';
454 *q++ = 'a';
455 left -= 2;
456 break;
457 case '\b':
458 *q++ = '\\';
459 *q++ = 'b';
460 left -= 2;
461 break;
462 case '\v':
463 *q++ = '\\';
464 *q++ = 'v';
465 left -= 2;
466 break;
467 default:
468 sprintf ( q, "\\%#lx", ic );
469 q += 5;
470 left -= 5;
471 } /* switch */
472 } /* unprintable */
473 } /* while */
474
475 *q = '\0';
476 /*BLATHER2 ( "printable returning %d: '%s'", strlen(printable_buffer), printable_buffer );*/
477 return printable_buffer;
478 }
479
480 void
open_trace(char * tname)481 open_trace ( char *tname )
482 {
483 UID_T uid_save = -1;
484 UID_T myuid = -1;
485
486
487 uid_save = geteuid();
488 myuid = getuid();
489 if ( SETEUID ( myuid ) != 0 )
490 adios ( HERE, "internal error @ %i", __LINE__ );
491
492 trace_file = fopen ( tname, "a+" );
493 if ( trace_file == NULL )
494 adios ( HERE, "Unable to open trace file \"%s\": %s (%d)\n",
495 tname, STRERROR(errno), errno );
496 BLATHER1 ( "Trace and Debug destination is file \"%s\"",
497 tname );
498
499 if ( SETEUID ( uid_save ) != 0 )
500 adios ( HERE, "internal error @ %i", __LINE__ );
501 }
502
503
504 #ifndef HAVE_STRDUP
505 #include <stddef.h>
506
507 char *
strdup(str)508 strdup(str)
509 char *str;
510 {
511 int len;
512 char *copy;
513
514 len = strlen(str) + 1;
515 if ( ! ( copy = malloc ( (u_int) len ) ) )
516 return ( (char *) NULL );
517 bcopy ( str, copy, len );
518 return ( copy );
519 }
520 #endif /* not HAVE_STRDUP */
521
522
523 /*
524 * Obscure password so a cleartext search doesn't come up with
525 * something interesting.
526 *
527 */
528 char *
obscure(string)529 obscure ( string )
530 char *string;
531 {
532 unsigned char *cp, *newstr;
533
534 cp = newstr = (unsigned char *) strdup ( string );
535
536 while ( *cp ) {
537 *cp++ ^= 0xff;
538 }
539
540 return ( (char *) newstr );
541 }
542
543
544 /* Use GNU_PASS for longer passwords on systems that support termios */
545
546 #ifndef GNU_PASS
547 char *getpass();
548 #else /* not GNU_PASS */
549
550 /* Copyright (C) 1992, 1993, 1994 Free Software Foundation, Inc.
551 * This file is part of the GNU C Library.
552
553 * The GNU C Library is free software; you can redistribute it and/or
554 * modify it under the terms of the GNU Library General Public License as
555 * published by the Free Software Foundation; either version 2 of the
556 * License, or (at your option) any later version.
557 */
558 /* It is desireable to use this bit on systems that have it.
559 The only bit of terminal state we want to twiddle is echoing, which is
560 done in software; there is no need to change the state of the terminal
561 hardware. */
562
563 # include <stdio.h>
564 # include <termios.h>
565
566 # ifndef TCSASOFT
567 # define TCSASOFT 0
568 # endif
569
570 # ifdef SSIZET
571 typedef SSIZET ssize_t;
572 # endif
573
574
575 char *
getpass(prompt)576 getpass (prompt)
577 # if defined(HPUX)
578 char *prompt;
579 # else
580 const char *prompt;
581 # endif /* defined(HPUX) */
582 {
583 FILE *in, *out;
584 struct termios t;
585 int echo_off;
586 static char *buf = NULL;
587 static size_t bufsize = 0;
588 ssize_t nread;
589
590 /* Try to write to and read from the terminal if we can.
591 If we can't open the terminal, use stderr and stdin. */
592
593 in = fopen ("/dev/tty", "w+");
594 if (in == NULL)
595 {
596 in = stdin;
597 out = stderr;
598 }
599 else
600 out = in;
601
602 /* Turn echoing off if it is on now. */
603
604 if (tcgetattr (fileno (in), &t) == 0)
605 {
606 if (t.c_lflag & ECHO)
607 {
608 t.c_lflag &= ~ECHO;
609 echo_off = tcsetattr (fileno (in), TCSAFLUSH|TCSASOFT, &t) == 0;
610 t.c_lflag |= ECHO;
611 }
612 else
613 echo_off = 0;
614 }
615 else
616 echo_off = 0;
617
618 /* Write the prompt. */
619 fputs (prompt, out);
620 fflush (out);
621
622 /* Read the password. */
623 # ifdef NO_GETLINE
624 bufsize = 256;
625 buf = (char *)malloc(256);
626 nread = (fgets(buf, (size_t)bufsize, in) == NULL) ? 1 : strlen(buf);
627 rewind(in);
628 fputc('\n', out);
629 # else
630 nread = __getline (&buf, &bufsize, in);
631 # endif /* NO_GETLINE */
632
633 if ( nread < 0 && buf != NULL )
634 buf[0] = '\0';
635 else if ( buf[nread - 1] == '\n' )
636 /* Remove the newline. */
637 buf[nread - 1] = '\0';
638
639 /* Restore echoing. */
640 if (echo_off)
641 (void) tcsetattr (fileno (in), TCSAFLUSH|TCSASOFT, &t);
642
643 if (in != stdin)
644 /* We opened the terminal; now close it. */
645 fclose (in);
646
647 return buf;
648 }
649 #endif /* not GNU_PASS */
650
651
652 /* ARGSUSED */
653
654 int
main(argc,argv)655 main ( argc, argv )
656 int argc;
657 char *argv[];
658 {
659 UID_T myuid = -1;
660 int flags = 0,
661 i = 0,
662 apoplen = 0,
663 scramlen = 0;
664 BOOL delesw = FALSE,
665 initsw = FALSE,
666 safesw = FALSE,
667 onlysw = FALSE,
668 passtype = FALSE,
669 insist = FALSE,
670 listsw = FALSE,
671 usersw = FALSE,
672 dumpsw = FALSE,
673 popuser = FALSE, /* TRUE: we are the db owner */
674 user_found = FALSE; /* TRUE: user to be changed exists */
675 switchType v = BAD_SW;
676 char *cp = NULL,
677 *userid = NULL,
678 *givenpassword = NULL,
679 *db_name = NULL,
680 apopfld [ BUFSIZ ] = "",
681 scramfld [ BUFSIZ ] = "",
682 outfld [ BUFSIZ ] = "",
683 buf [ 100 ] = "",
684 obuf [ 100 ] = "";
685 struct passwd *pw = NULL,
686 pop_pw,
687 my_pw;
688 datum key,
689 value;
690
691 #ifdef SCRAM
692 SCRAM_MD5_VRFY scram_verifiers;
693 #endif /* SCRAM */
694
695 #ifdef GDBM
696 GDBM_FILE db;
697 char auth_file[BUFSIZ];
698 #else /* not GDBM */
699 DBM *db;
700 char auth_dir[BUFSIZ];
701 # ifndef BSD44_DBM
702 char auth_pag[BUFSIZ];
703 # endif /* BSD44_DBM */
704 #endif /* GDBM */
705
706 int f,
707 mode;
708
709
710 memset ( &pop_pw, 0, sizeof(pop_pw) );
711 memset ( &my_pw, 0, sizeof(my_pw) );
712 umask ( 0077 ); /* make sure we don't create group- or world-writable files */
713 srandom ( (unsigned int) time ( (TIME_T *) 0) ); /* seed random with the
714 current time */
715
716 cp = program = argv[0];
717 while ( *program )
718 if ( *(program++) == '/' )
719 cp = program;
720 program = argv[0];
721
722 /*
723 * Open the log file
724 */
725 #ifdef SYSLOG42
726 openlog ( program, 0 );
727 #else
728 openlog ( program, POP_LOGOPTS, POP_FACILITY );
729 #endif
730
731 /*
732 * See if we're in APOP, SCRAM, or default mode
733 */
734 mode = 0;
735 while ( modes[mode].name &&
736 strcmp(cp, modes[mode].name ) )
737 mode++;
738
739 if ( mode >= OTHER )
740 adios ( HERE, "Unknown mode: %s", cp );
741 else if ( mode == POP_AUTH || mode == APOP_AUTH ) {
742 passtype = 0;
743 onlysw = TRUE; /* popauth always change ONLY the apop
744 password */
745 }
746 else if ( mode == SCRAM_AUTH ) {
747 passtype = 1;
748 onlysw = FALSE; /* scramauth by default also clears the apop
749 password */
750 }
751
752 BLATHER3 ( "mode=%s; passtype=%d; onlysw=%s",
753 modes[mode].name, passtype, onlysw ? "true" : "false" );
754
755 /*
756 * Process arguments
757 */
758 argv++;
759 argc--;
760
761 while ( argc > 0 ) {
762 cp = argv[0];
763
764 if ( *cp != '-' ) {
765 fprintf ( stderr, "%s: unrecognized switch \"%s\"", program, cp );
766 helpful();
767 }
768
769 cp++;
770 i = 0;
771 v = BAD_SW;
772 for ( i = 0; switches[i].name; i++ ) {
773 if ( strcmp ( cp, switches[i].name ) == 0 ) {
774 v = switches[i].val;
775 break;
776 }
777 }
778
779 if ( switches[i].param > 0 ) {
780 if ( argc < 2 || argv[1][0] == '-' )
781 adios ( HERE, "missing argument for %s", argv[0] );
782 }
783
784 BLATHER1 ( "...processing switch '-%s'", cp );
785
786 switch ( v ) {
787 default:
788 fprintf ( stderr, "%s: \"-%s\" unknown option\n", program, cp );
789 helpful();
790 case TRACESW:
791 debug++;
792 open_trace ( argv[1] );
793 argc--;
794 argv++;
795 break;
796 case DEBUGSW:
797 debug = TRUE;
798 break;
799 case VERSNSW:
800 printf ( "%s v%s\n", program, VERSION );
801 byebye ( 0 );
802 break;
803 case INITSW:
804 initsw = TRUE;
805 listsw = FALSE;
806 delesw = FALSE;
807 break;
808 case SAFESW:
809 safesw = TRUE;
810 break;
811 case DUMPSW:
812 dumpsw = TRUE;
813 case LISTSW:
814 listsw = TRUE;
815 initsw = FALSE;
816 delesw = FALSE;
817 if ( argc >= 2 && argv[1][0] != '-' ) {
818 userid = argv[1];
819 argc--;
820 argv++;
821 }
822 break;
823 case ONLYSW:
824 onlysw = TRUE;
825 break;
826 case DELESW:
827 delesw = TRUE;
828 initsw = FALSE;
829 listsw = FALSE;
830 userid = argv[1];
831 argc--;
832 argv++;
833 break;
834 case USERSW:
835 usersw = TRUE;
836 userid = argv[1];
837 if ( argc >= 3 && argv[2][0] != '-' ) {
838 givenpassword = argv[2];
839 argc--;
840 argv++;
841 }
842 argc--;
843 argv++;
844 break;
845 case HELPSW:
846 helpful();
847 break;
848 } /* switch (v) */
849
850 argc--;
851 argv++;
852 } /* while argc > 0 */
853
854
855 /* Expand the rest iff SCRAM or APOP and favor SCRAM to hold auth db name */
856
857 #ifdef SCRAM
858 # define AUTHON
859 # define AUTHDB SCRAM
860 #else
861 # ifdef APOP
862 # define AUTHON
863 # define AUTHDB APOP
864 # endif /* APOP */
865 #endif /* SCRAM */
866
867 #ifndef AUTHON
868 adios ( HERE, "not compiled with either SCRAM or APOP options" );
869 #else
870 # ifndef SCRAM
871 if ( mode == SCRAM_AUTH )
872 adios ( HERE, "not compiled with SCRAM option" );
873 # endif
874 # ifndef APOP
875 if ( (mode == APOP_AUTH) || (mode == POP_AUTH) )
876 adios ( HERE, "not compiled with APOP option" );
877 # endif
878
879 myuid = getuid();
880 pw = getpwuid ( myuid );
881 if ( pw == NULL)
882 adios ( HERE, "Sorry, don't know who you (uid %d) are.", myuid );
883 my_pw = *pw;
884 my_pw.pw_name = strdup ( pw->pw_name );
885
886 pw = getpwnam ( POPUID );
887 if ( pw == NULL)
888 adios ( HERE, "\"%s\": userid unknown", POPUID );
889 pop_pw = *pw;
890
891 if ( pop_pw.pw_uid == myuid )
892 popuser = TRUE;
893
894 if ( myuid != 0 && popuser == FALSE
895 && ( delesw || ( userid != NULL && strcmp(userid, my_pw.pw_name) != 0 )
896 || ( usersw && userid != NULL ) )
897 )
898 adios ( HERE,
899 "Only superuser or user \"%s\" can perform the requested function",
900 POPUID );
901 #ifndef __CYGWIN__
902 if ( myuid != 0 && ( initsw || dumpsw ) )
903 #else
904 if ( popuser == FALSE && ( initsw || dumpsw ) )
905 #endif
906 adios ( HERE, "Only superuser can perform the requested function" );
907
908 if ( delesw )
909 fprintf ( stderr, "Warning: deleting user \"%s\"\n",
910 userid );
911
912 #ifdef GDBM
913 strlcpy ( auth_file, AUTHDB, sizeof(auth_file) - 1 );
914 BLATHER1 ( "GDBM: auth_file=%s", auth_file );
915 db_name = auth_file;
916 #else /* not GDBM */
917 strlcpy ( auth_dir, AUTHDB, sizeof(auth_dir) - 5 );
918 # ifdef BSD44_DBM
919 strcat ( auth_dir, ".db" );
920 BLATHER1 ( "BSD44_DBM: auth_dir=%s", auth_dir );
921 db_name = auth_dir;
922 # else /* non-BSD44 DBM */
923 strlcpy ( auth_pag, AUTHDB, sizeof(auth_pag) - 5 );
924 strcat ( auth_pag, ".pag" );
925 strcat ( auth_dir, ".dir" );
926 BLATHER2 ( "xDBM: auth_pag='%s'; auth_dir='%s'", auth_pag, auth_dir );
927 db_name = malloc ( sizeof ( auth_pag ) + 15 );
928 if ( db_name != NULL ) {
929 strcpy ( db_name, auth_pag );
930 strcat ( db_name, " (and .dir )" );
931 } else
932 db_name = auth_pag;
933 # endif /* BSD44_DBM */
934 #endif /* GDBM */
935
936 if ( delesw ) {
937 if ( myuid && !popuser )
938 adios ( HERE, "Only root or %s may delete entries", POPUID );
939
940 #ifdef GDBM
941 db = gdbm_open ( auth_file, 512, GDBM_WRITER, 0, 0 );
942 #else
943 db = dbm_open ( AUTHDB, O_RDWR, 0 );
944 #endif
945
946 if ( db == NULL )
947 adios ( HERE,
948 "%s: unable to open POP authentication DB: %s (%i) [%i]",
949 AUTHDB, strerror(errno), errno, __LINE__ );
950
951 key.dptr = userid;
952 key.dsize = strlen ( key.dptr ) + 1;
953 BLATHER2 ( "...fetching: (%d) '%s'",
954 key.dsize, printable ( key.dptr, key.dsize ) );
955
956 #ifdef GDBM
957 value = gdbm_fetch ( db, key );
958 #else
959 value = dbm_fetch ( db, key );
960 #endif
961
962 check_db_err ( db, "fetch", TRUE );
963
964 if ( value.dptr == NULL )
965 adios ( HERE, "User '%s' not found in authentication database", userid );
966
967 #ifdef GDBM
968 if ( gdbm_delete ( db, key ) < 0 )
969 #else
970 if ( dbm_delete ( db, key ) < 0 )
971 #endif
972 adios ( HERE, "Unable to delete user '%s' from authentication database",
973 userid );
974
975 #ifdef GDBM
976 gdbm_close ( db );
977 #else
978 dbm_close ( db );
979 #endif
980
981 logit ( trace_file, POP_NOTICE, HERE,
982 "popauth: user \"%s\" deleted by \"%s\" from db \"%s\"",
983 userid, my_pw.pw_name, AUTHDB );
984 byebye ( 0 );
985 } /* delesw */
986
987 if ( initsw ) {
988 struct stat st;
989
990 setuid ( myuid );
991
992 #ifdef GDBM
993 if ( stat ( auth_file, &st ) != -1 )
994 #else
995 if ( stat ( auth_dir, &st ) != -1 )
996 #endif
997 {
998 char ibuf [ 30 ];
999
1000 if ( safesw == TRUE ) {
1001 fprintf ( stderr, "POP authentication DB not "
1002 "initialized (exists): %s\n",
1003 db_name );
1004 byebye ( 0 );
1005 }
1006 printf ( "Really initialize POP authentication DB? " );
1007 if ( fgets ( ibuf, sizeof(ibuf), stdin ) == NULL || ibuf[0] != 'y' )
1008 byebye ( 1 );
1009 #ifdef GDBM
1010 (void) unlink ( auth_file );
1011 #else
1012 (void) unlink ( auth_dir );
1013 # ifndef BSD44_DBM
1014 (void) unlink ( auth_pag );
1015 # endif
1016 #endif
1017 logit ( trace_file, POP_NOTICE, HERE,
1018 "popauth: user \"%s\" removed existing db \"%s\"",
1019 my_pw.pw_name, AUTHDB );
1020 }
1021
1022 #ifdef GDBM
1023 db = gdbm_open ( auth_file, 512, GDBM_WRCREAT, 0600, 0 );
1024 if ( db == NULL )
1025 adios ( HERE,
1026 "unable to create POP authentication DB %s: %s (%d) [%i] ",
1027 auth_file, strerror(errno), errno, __LINE__ );
1028 gdbm_close ( db );
1029 if ( chown ( auth_file, pop_pw.pw_uid, pop_pw.pw_gid ) == -1)
1030 adios ( HERE,
1031 "error setting ownership of POP authentication for %s: %s (%d) [%d]",
1032 auth_file, strerror(errno), errno, __LINE__ );
1033 #else
1034 db = dbm_open ( AUTHDB, O_RDWR | O_CREAT, 0600 );
1035 if ( db == NULL )
1036 adios ( HERE, "unable to create POP authentication DB %s: %s (%d) [%d]",
1037 AUTHDB, strerror(errno), errno, __LINE__ );
1038 dbm_close ( db );
1039 if ( chown ( auth_dir, pop_pw.pw_uid, pop_pw.pw_gid ) == -1
1040 # ifndef BSD44_DBM
1041 || chown (auth_pag, pop_pw.pw_uid, pop_pw.pw_gid) == -1
1042 # endif
1043 )
1044 adios ( HERE, "error setting ownership of POP authentication DB %s: %s (%d) [%d]",
1045 AUTHDB, strerror(errno), errno, __LINE__ );
1046 #endif /* GDBM */
1047
1048 logit ( trace_file, POP_NOTICE, HERE,
1049 "popauth: user \"%s\" initialized db \"%s\"",
1050 my_pw.pw_name, AUTHDB );
1051 byebye ( 0 );
1052 } /* initsw */
1053
1054 #ifdef GDBM
1055 db = gdbm_open ( auth_file, 512, GDBM_READER, 0, 0 );
1056 if ( db == NULL)
1057 adios ( HERE, "unable to open POP authentication DB %s:\n\t%s (%i) [%i]",
1058 auth_file, strerror(errno), errno, __LINE__ );
1059 #else
1060 db = dbm_open ( AUTHDB, O_RDONLY, 0 );
1061 if ( db == NULL )
1062 adios ( HERE, "unable to open POP authentication DB %s:\n\t%s (%i) [%i]",
1063 AUTHDB, strerror(errno), errno, __LINE__ );
1064 #endif
1065
1066 #ifdef GDBM
1067 f = open ( auth_file, listsw ? O_RDONLY : O_RDWR );
1068 if ( f == -1 )
1069 adios ( HERE, "%s: unable to open POP authentication DB:\n\t%s (%i) [%i]",
1070 auth_file, strerror(errno), errno, __LINE__ );
1071 #else
1072 f = open ( auth_dir, listsw ? O_RDONLY : O_RDWR );
1073 if ( f == -1 )
1074 adios ( HERE, "%s: unable to open POP authentication DB:\n\t%s (%d) [%d]",
1075 auth_dir, strerror(errno), errno, __LINE__ );
1076 if ( flock ( f, LOCK_SH ) == -1 )
1077 adios ( HERE, "%s: unable to lock POP authentication DB:\n\t%s (%d) [%d]",
1078 auth_dir, strerror(errno), errno, __LINE__ );
1079 #endif
1080
1081 if ( listsw ) {
1082 if ( userid == NULL )
1083 userid = my_pw.pw_name;
1084
1085 if ( strcmp ( userid, "ALL" ) != 0 ) { /* list one user */
1086 key.dptr = userid;
1087 key.dsize = strlen ( key.dptr ) + 1;
1088 BLATHER2 ( "...fetching: (%d) '%s'",
1089 key.dsize, printable ( key.dptr, key.dsize ) );
1090
1091 #ifdef GDBM
1092 value = gdbm_fetch ( db, key );
1093 #else
1094 value = dbm_fetch ( db, key );
1095 #endif
1096
1097 check_db_err ( db, "fetch", TRUE );
1098
1099 if ( value.dptr == NULL )
1100 adios ( HERE, "no entry for \"%s\" in POP authentication DB", userid );
1101
1102 strcpy ( apopfld, value.dptr );
1103 if ( (size_t) value.dsize > strlen(apopfld) + 1 )
1104 strcpy ( scramfld, &((char *) value.dptr) [strlen(apopfld) + 1] );
1105 else
1106 scramfld[0] = '\0';
1107 apoplen = strlen ( apopfld ) + 1;
1108 scramlen = strlen ( scramfld ) + 1;
1109
1110 if ( dumpsw ) {
1111 printf ( "%.16s:%*s %.62s\n",
1112 (char *) key.dptr,
1113 16 - (int)strlen(key.dptr), " ",
1114 printable ( value.dptr, value.dsize ) );
1115 BLATHER5 ( "Dumping \"%s\": (%d)'%s'; APOP %d; SCRAM %d",
1116 key.dptr,
1117 value.dsize, printable ( value.dptr, value.dsize ),
1118 apoplen, scramlen );
1119 } else {
1120 printf ( "%-16s: %s %s\n", (char *) key.dptr,
1121 ( apoplen > 1 ) ? "APOP" : " ",
1122 ( scramlen > 1 ) ? "SCRAM" : "" );
1123 BLATHER3 ( "Listing user \"%s\": APOP %s; SCRAM %s",
1124 key.dptr,
1125 ( apoplen > 1 ) ? "yes" : "no",
1126 ( scramlen > 1 ) ? "yes" : "no" );
1127 }
1128
1129 } /* list one user */
1130 else { /* list all users */
1131 BLATHER0 ( "Listing all users... " );
1132 #ifdef GDBM
1133 for ( key = gdbm_firstkey ( db ); key.dptr; key = gdbm_nextkey ( db, key ) )
1134 #else
1135 for ( key = dbm_firstkey ( db ); key.dptr; key = dbm_nextkey ( db ) )
1136 #endif
1137 {
1138 BLATHER2 ( "...fetching: (%d)'%s'",
1139 key.dsize, printable ( key.dptr, key.dsize ) );
1140
1141 #ifdef GDBM
1142 value = gdbm_fetch ( db, key );
1143 #else
1144 value = dbm_fetch ( db, key );
1145 #endif
1146
1147 check_db_err ( db, "fetch", FALSE );
1148
1149 if ( value.dptr == NULL ) {
1150 printf ( "%.16s:%*s *** no information?!?\n",
1151 (char *) key.dptr, 16 - (int)strlen(key.dptr), " " );
1152 BLATHER1 ( "*** No information for user \"%s\"", buf );
1153 }
1154 else {
1155 strcpy ( apopfld, value.dptr );
1156 if ( (size_t) value.dsize > strlen(apopfld) + 1 )
1157 strcpy ( scramfld, &((char *) value.dptr) [strlen(apopfld) + 1] );
1158 else
1159 scramfld[0] = '\0';
1160 apoplen = strlen ( apopfld ) + 1;
1161 scramlen = strlen ( scramfld ) + 1;
1162
1163 if ( dumpsw ) {
1164 printf ( "%.16s:%*s %.62s\n",
1165 (char *) key.dptr,
1166 16 - (int)strlen(key.dptr), " ",
1167 printable ( value.dptr, value.dsize ) );
1168 BLATHER5 ( "Dumping \"%s\": (%d)'%s'; APOP %d; SCRAM %d",
1169 key.dptr,
1170 value.dsize, printable ( value.dptr, value.dsize ),
1171 apoplen, scramlen );
1172 } else {
1173 printf ( "%-16s: %s %s\n", (char *) key.dptr,
1174 ( apoplen > 1 ) ? "APOP" : " ",
1175 ( scramlen > 1 ) ? "SCRAM" : "" );
1176 BLATHER3 ( "Listing user \"%s\": APOP %s; SCRAM %s",
1177 key.dptr,
1178 ( apoplen > 1 ) ? "yes" : "no",
1179 ( scramlen > 1 ) ? "yes" : "no" );
1180 }
1181
1182 }
1183 } /* for loop */
1184
1185 /*
1186 * Check if we terminated normally or on an error
1187 */
1188 check_db_err ( db, "first/next key", TRUE );
1189
1190 } /* list all users */
1191
1192 #ifdef GDBM
1193 gdbm_close ( db );
1194 #else
1195 dbm_close ( db );
1196 #endif
1197
1198 byebye ( 0 );
1199 } /* listsw */
1200
1201 /*
1202 * If we're here, we must be doing a password change (usersw)
1203 */
1204 if ( userid == NULL ) {
1205 userid = my_pw.pw_name;
1206 } else {
1207 pw = getpwnam ( userid );
1208 if ( pw == NULL )
1209 adios ( HERE, "Sorry, don't know who user %s is.", userid );
1210 userid = pw->pw_name;
1211 }
1212
1213 key.dptr = userid;
1214 key.dsize = strlen ( key.dptr ) + 1;
1215 BLATHER2 ( "...fetching: (%d) '%s'",
1216 key.dsize, printable ( key.dptr, key.dsize ) );
1217
1218 #ifdef GDBM
1219 value = gdbm_fetch ( db, key );
1220 #else
1221 value = dbm_fetch ( db, key );
1222 #endif
1223
1224 check_db_err ( db, "fetch", TRUE );
1225 user_found = ( value.dptr != NULL );
1226
1227 if ( user_found ) {
1228 strcpy ( apopfld, value.dptr );
1229 if ( (size_t) value.dsize > strlen(apopfld) + 1 )
1230 strcpy ( scramfld, &((char *) value.dptr) [strlen(apopfld) + 1] );
1231 else
1232 scramfld[0] = '\0';
1233
1234 BLATHER3 ( "user %s exists in db (%d/%d)",
1235 userid, strlen(apopfld), strlen(scramfld) );
1236 }
1237 else {
1238 apopfld[0] = scramfld[0] = '\0';
1239 BLATHER1 ( "user %s doesn't exist -- adding", userid );
1240 }
1241
1242 fprintf ( stderr, "%s %s%s %s for %s.\n",
1243 user_found ? "Changing" : "Adding",
1244 onlysw ? "only " : "",
1245 (mode == SCRAM_AUTH) ? "SCRAM" : "APOP",
1246 passtype ? "pass phrase" : "password",
1247 userid );
1248
1249 apoplen = strlen ( apopfld ) + 1;
1250 scramlen = strlen ( scramfld ) + 1;
1251
1252 /*
1253 * If user isn't root or the db owner, and user has an existing
1254 * password for the mode (apop or scarm) being changed, verify old
1255 * password.
1256 */
1257 if ( ( myuid && !popuser )
1258 && (((mode==APOP_AUTH || mode==POP_AUTH) && apopfld[0] )
1259 ||(mode==SCRAM_AUTH && scramfld[0] )) ) {
1260
1261 if ( !(i=strlen(strncpy(obuf,
1262 getpass(passtype?"Old pass phrase:":"Old password:"),sizeof(obuf)))) )
1263 adios ( HERE, "Sorry, password entered incorrectly\n" );
1264
1265 if ( mode == APOP_AUTH || mode == POP_AUTH ) {
1266 if ( ((apoplen - 1) != i) ||
1267 (strncmp(obuf, apopfld, i) &&
1268 strncmp(obuf, obscure(apopfld), i)) )
1269 adios ( HERE, "Sorry, password entered incorrectly\n" );
1270 }
1271 else if ( mode == SCRAM_AUTH ) {
1272 #ifdef SCRAM
1273 SCRAM_MD5_VRFY test_verifiers;
1274
1275 scramlen = sizeof( scram_verifiers );
1276
1277 if ( decode64( scramfld, strlen(scramfld),
1278 (char *) &scram_verifiers, &scramlen )
1279 || scramlen != sizeof( scram_verifiers ) )
1280 adios ( HERE, "unable to decode SCRAM verifier from the authenication DB" );
1281
1282 scram_md5_vgen ( &test_verifiers,
1283 scram_verifiers.salt,
1284 obuf,
1285 0,
1286 1,
1287 NULL );
1288
1289 if ( memcmp( &scram_verifiers, &test_verifiers, sizeof(scram_verifiers)) )
1290 #endif
1291 adios ( HERE, "Sorry, pass phrase entered incorrectly\n");
1292 }
1293 } /* old password verification */
1294
1295 #ifdef GDBM
1296 gdbm_close ( db );
1297 #else
1298 dbm_close ( db );
1299 if ( flock ( f, LOCK_UN ) == -1)
1300 adios ( HERE, "%s: unable to unlock POP authentication DB\n\t%s (%d) [%d]",
1301 auth_dir, strerror(errno), errno, __LINE__ );
1302 #endif /* GDBM */
1303
1304 for ( insist = 0; insist < 2; insist++ ) {
1305 int i;
1306 char c;
1307
1308 if ( insist )
1309 printf ( "Please use %s.\n",
1310 flags == 1 ? "at least one non-numeric character"
1311 : ( passtype ? "a longer pass phrase"
1312 : "a longer password" ) );
1313
1314 /*
1315 * If new password passed in (givenpassword) use it,
1316 * otherwise prompt for the new one.
1317 */
1318 if ( givenpassword != NULL ) {
1319 strncpy ( buf, givenpassword, sizeof ( buf ) );
1320 buf [ sizeof(buf) -1 ] = '\0';
1321 i = strlen ( buf );
1322 }
1323 else {
1324 strncpy ( buf,
1325 getpass ( passtype ? "New pass phrase:"
1326 : "New password:" ),
1327 sizeof ( buf ) );
1328 buf [ sizeof(buf) -1 ] = '\0';
1329 i = strlen ( buf );
1330 if ( i == 0 || strncmp ( buf, obuf, i ) == 0 ) {
1331 fprintf ( stderr, passtype ? "Pass phrase unchanged.\n"
1332 : "Password unchanged.\n" );
1333 byebye (1);
1334 }
1335 }
1336
1337 if ( !strcmp(buf,"*") )
1338 break;
1339
1340 flags = 0;
1341 for ( (cp = buf); (c = *cp++); )
1342 if ( c >= 'a' && c <= 'z' )
1343 flags |= 2;
1344 else
1345 if ( c >= 'A' && c <= 'Z' )
1346 flags |= 4;
1347 else
1348 if ( c >= '0' && c <= '9' )
1349 flags |= 1;
1350 else
1351 flags |= 8;
1352
1353 if ( givenpassword != NULL
1354 || ( flags >= 7 && i >= 4 )
1355 || ( (flags == 2 || flags == 4 ) && i >= 6 )
1356 || ( (flags == 3 || flags == 5 || flags == 6 ) && i >= 5 )
1357 )
1358 break;
1359 } /* for loop on insist */
1360
1361
1362 if ( givenpassword == NULL ) {
1363 if ( strcmp ( buf,
1364 getpass ( passtype ? "Retype new pass phrase:"
1365 : "Retype new password:" ) ) ) {
1366 fprintf ( stderr,
1367 passtype ? "Mismatch -- pass phrase unchanged.\n"
1368 : "Mismatch -- password unchanged.\n" );
1369 byebye ( 1 );
1370 }
1371 }
1372
1373 if ( !strcmp(buf, "*") )
1374 buf[0] = '\0';
1375
1376 #ifdef GDBM
1377 db = gdbm_open ( auth_file, 512, GDBM_WRITER, 0, 0 );
1378 #else
1379 db = dbm_open ( AUTHDB, O_RDWR, 0 );
1380 #endif /* GDBM */
1381 if ( db == NULL )
1382 adios ( HERE, "%s: unable to open POP authentication DB:\n\t%s (%i) [%i]",
1383 AUTHDB, strerror(errno), errno, __LINE__ );
1384
1385 #ifdef FLOCK_EX
1386 # ifndef GDBM
1387 if ( flock ( f, LOCK_EX ) == - 1 )
1388 adios ( HERE, "%s: unable to lock POP authentication DB\n\t%s (%i) [%i]",
1389 auth_dir, strerror(errno), errno, __LINE__ );
1390 # endif
1391 #endif /* FLOCK_EX */
1392
1393 key.dsize = strlen ( key.dptr = userid ) + 1;
1394
1395 if ( mode == APOP_AUTH || mode == POP_AUTH ) {
1396 strcpy ( apopfld, obscure(buf) );
1397 }
1398
1399 else if ( mode == SCRAM_AUTH ) {
1400 scramlen = 0;
1401
1402 if ( buf[0] ) {
1403
1404 #ifdef SCRAM
1405 UINT4 key[4];
1406 UINT4 data[4];
1407 unsigned char salt[ SCRAM_MD5_SALTSIZE ];
1408 int x;
1409 unsigned char digest[ HMAC_MD5_SIZE ];
1410
1411 key[ 0 ] = (UINT4) random(); /* seeded with start up time */
1412 key[ 1 ] = (UINT4) time((time_t *)0);/* include current time */
1413
1414 /* seed random with the current time to nearest second */
1415 srandom( (unsigned int) time((TIME_T *)0) );
1416
1417 key[ 3 ] = (UINT4) random();
1418 key[ 4 ] = (UINT4) random();
1419
1420 data[ 0 ] = (UINT4) getpid(); /* pid+uid+most of user name */
1421 data[ 1 ] = myuid;
1422 strncpy( (char *)&data[2], userid, 2*sizeof(UINT4) );
1423 /* personal info as data */
1424 /* random()& time() as key*/
1425 hmac_md5( (unsigned char *)data, sizeof( data ),
1426 (unsigned char *)key, sizeof( key ),
1427 digest );
1428
1429 /* zero out the salt */
1430 /* and then fold in the digest */
1431
1432 memset(salt, '\0', SCRAM_MD5_SALTSIZE );
1433 x = HMAC_MD5_SIZE;
1434 while ( x-- )
1435 salt[ x % SCRAM_MD5_SALTSIZE ] ^= digest[ x ];
1436
1437 scram_md5_vgen( &scram_verifiers,
1438 salt,
1439 buf,
1440 0,
1441 1,
1442 NULL );
1443
1444 scramlen = sizeof( scramfld );
1445
1446 if ( encode64( (char *) &scram_verifiers, sizeof(scram_verifiers),
1447 scramfld, &scramlen ) || scramlen >= (int) sizeof(scramfld) )
1448 #endif /* SCRAM */
1449 adios ( HERE, "unable to encode SCRAM verifier for the authenication DB" );
1450 } /* buf[0] */
1451
1452 scramfld [ scramlen ] = '\0';
1453
1454 if ( !onlysw )
1455 apopfld[0] = '\0';
1456 } /* mode == SCRAM_AUTH */
1457
1458 value.dsize = strlen ( value.dptr = strcpy(outfld, apopfld) ) + 1;
1459 value.dsize += strlen ( strcpy(&outfld[value.dsize], scramfld) ) + 1;
1460
1461 # ifdef GDBM
1462 i = gdbm_store ( db, key, value, GDBM_REPLACE );
1463 check_db_err ( db, "store", FALSE );
1464 gdbm_close ( db );
1465 # else /* not GDBM */
1466 i = dbm_store ( db, key, value, DBM_REPLACE );
1467 check_db_err ( db, "store", FALSE );
1468 dbm_close ( db );
1469 # endif /* GDBM */
1470
1471 check_db_err ( db, "close", FALSE );
1472
1473 if ( i )
1474 adios ( HERE, "POP authentication DB %s may be corrupt?!?", db_name );
1475
1476 #endif /* AUTHON */
1477
1478 logit ( trace_file, POP_NOTICE, HERE,
1479 "popauth: %s %s %s%s %s for %s",
1480 my_pw.pw_name,
1481 user_found ? "changed" : "added",
1482 onlysw ? "only " : "",
1483 (mode == SCRAM_AUTH) ? "SCRAM" : "APOP",
1484 passtype ? "pass phrase" : "password",
1485 userid );
1486
1487 byebye ( 0 );
1488 return 0; /* avoid a warning */
1489 }
1490
1491
1492 #ifndef HAVE_STRERROR
1493 char *
strerror(e)1494 strerror(e)
1495 int e;
1496 {
1497 extern char *sys_errlist[];
1498 extern int sys_nerr;
1499
1500 if(e < sys_nerr)
1501 return(sys_errlist[e]);
1502 else
1503 return("unknown error");
1504 }
1505 #endif /* HAVE_STRERROR */
1506