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