/** Fichier db.c ** ** Routines de gestion des mots de passe et des fichiers ** de configuration pour calife. ** ** Copyright (c) 1991-1995 by Ollivier ROBERT ** A distribuer selon les regles de la GNU GPL General Public Licence ** Voir le fichier COPYING fourni. ** **/ #include "config.h" /* GNU configure */ /* fichier de configuration */ #include "conf.h" #ifndef lint static char * rcsid = "$Id: //depot/security/calife/main/db.c#25 $"; #endif /* lint */ /** On cherche et ouvre la database locale. ** ** Dans tous les cas, le fichier log est ouvert (il est local). Si syslogd(8) ** est actif on ouvre le "fichier" pour syslog(3). Le fichier de log devient ** maintenant dependant de NO_SYSLOG: s'il n'y a pas de syslog d'utilisable ** alors on ouvre le fichier de log. ** ** Parametres : neant ** ** Retourne : int s'il y a un probleme renvoie 1 sinon 0. **/ #ifdef STDC_HEADERS int open_databases (void) #else /* !STDC_HEADERS */ int open_databases () #endif { #ifdef DEBUG fprintf (stderr, "Opening databases...\n"); fprintf (stderr, "%s\n", rcsid); fflush (stderr); #endif /* * become root again */ GET_ROOT; if (access (AUTH_CONFIG_FILE, 0)) { #ifndef NO_SYSLOG syslog (LOG_AUTH | LOG_ERR, "No database in `%s', launching su...\n", AUTH_CONFIG_FILE); #endif /* NO_SYSLOG */ return 1; } else { /* * open log file */ #ifdef NO_SYSLOG if (!(logfile = fopen (ADMIN_LOG, "a+"))) fprintf (stderr, "Can't open file `%s' errno=`%d'", ADMIN_LOG, errno); else fchmod (fileno (logfile), 0600); #endif /* NO_SYSLOG */ /* open protected database */ fp = fopen (AUTH_CONFIG_FILE, "r"); if (fp == NULL) die (6, "calife: internal error, fp == NULL at %d\n", __LINE__); fchmod (fileno (fp), 0400); } /* * stay non root for a time */ RELEASE_ROOT; return 0; } /** Verifie si l'utilisateur de nom name a le droit d'utiliser ** calife. ** ** Line format is : ** ** login[:][shell][:user1,user2,user3,...] ** ** Parametres : name char * nom de l'utilisateur ** user_to_be char * futur nom d'utilisateur ** ** Retourne : int 1 si l'utilisateur est autorise 0 sinon. **/ #ifdef STDC_HEADERS int verify_auth_info (char * name, char * user_to_be) #else /* !STDC_HEADERS */ int verify_auth_info (name, user_to_be) char * name; char * user_to_be; #endif { int allowed = 0, do_tok = 1; char * * user_list = NULL; size_t l_line = 0; int nb_users = 0; char * line, * ptr; char * line_name, * line_shell; /* * let's search if user allowed or not */ #ifdef HAVE_WORKING_SYSCONF l_line = (size_t) sysconf (_SC_LINE_MAX); /* returns long usually */ #else l_line = MAX_STRING; #endif /* HAVE_WORKING_SYSCONF */ line = (char *) xalloc (l_line); while (!feof (fp)) { int lastc; if (fgets (line, l_line, fp) == NULL) break; /* * remove trailing '\n' if present */ lastc = strlen (line) - 1; if (line [lastc] == '\n') line [lastc] = '\0'; /* * empty line or comment or null login -- not allowed but ignored */ if (!line || (*line) == '#' || (*line) == '\0') continue; MESSAGE_1 ("Line read = |%s|\n", line); line_name = line; /* * Look for first ':' */ ptr = strchr (line, ':'); if (ptr == NULL) { /* * No shell and no user list */ custom_shell = 0; if (strcmp (line_name, name)) /* not us */ continue; goto escape; } /* * I got a ':', put a '\0' instead. */ *ptr++ = '\0'; /* * Do we have anything behind ? */ if (*ptr == '\0') custom_shell = 0; MESSAGE_1 ("Current line_name = |%s|\n", line_name); if (strcmp (line_name, name)) /* not us */ continue; line_shell = ptr; /* * Look for another ':', maybe just after the first one */ ptr = strchr (line_shell, ':'); if (ptr == line_shell) custom_shell = 0; /* * If we got another one, put a '\0' instead */ if (ptr) *ptr++ = '\0'; /* * Analyze the shell candidate */ if ((*(line_shell) == '*')) /* LOCKED ACCOUNT */ { allowed = 0; goto end; } /* * look for pathname */ if ((*(line_shell) == '/')) { /* * Is this a real shell ? */ if (access (line_shell, R_OK | X_OK)) { MESSAGE_1 (" access of |%s| failed, no custom_shell\n", line_shell); custom_shell = 0; } else { custom_shell = 1; shell = (char *) xalloc (strlen (line_shell) + 1); strncpy (shell, line_shell, strlen (line_shell)); shell [strlen (line_shell)] = '\0'; MESSAGE_1 (" current line_shell = |%s|\n", line_shell); } } else { custom_shell = 0; do_tok = 0; } MESSAGE_1 (" custom_shell = |%d|\n", custom_shell); /* * Analyze the rest of the line */ if (ptr) { /* * we've got a user list */ if (*ptr) { char * p, ** q; MESSAGE_1 (" current user_list = |%s|\n", ptr); /* * the list should be user1,user2,...,user_n */ p = ptr; /* count users # in the list */ while (p && *p) if (*p++ == ',') nb_users++; if (*--p != ',') /* last char is not a ',' */ nb_users++; MESSAGE_1 (" users # = |%d|\n", nb_users); /* * two passes to avoid fixed size allocations */ user_list = (char **) calloc (nb_users + 1, sizeof (char *)); if (user_list == NULL) die (255, "Out of memory at line %d in file %s\n", __LINE__, __FILE__); /* * put pointers in user_list */ p = ptr; q = user_list; /* * 1st user is immediate */ (*q++) = p; while (p && *p) { if (*p == ',') /* found a ',' */ { *p = '\0'; if (*++p == ',') continue; (*q++) = p++; /* next char is user name */ } else p++; /* only regular letters */ } } } /* * user_list is complete, now compare */ escape: if (user_list) { int i; for ( i = 0; i < nb_users; i++ ) { MESSAGE_2(" comparing |%s| to |%s|\n", user_to_be, user_list [i]); if (!strcmp (user_list [i], user_to_be)) { allowed = 1; nb_users = 0; goto end; /* beuh but too many loops */ } } } else { allowed = 1; nb_users = 0; break; } } end: free (line); if (user_list) free (user_list); fclose (fp); return allowed; } /** Demande et verifie le mot de passe pour l'utilisateur name ** Si name == root on ne fait rien. ** ** Parametres : name char * nom de l'utilisateur ** user_to_be char * qui va-t-on devenir ** this_time char * quand ? ** tty char * sur quel tty ** ** Retourne : neant **/ #ifdef STDC_HEADERS void verify_password (char * name, char * user_to_be, char * this_time, char * tty) #else /* !STDC_HEADERS */ void verify_password (name, user_to_be, this_time, tty) char * name, * user_to_be, * this_time, * tty; #endif /* STDC_HEADERS */ { int i = 0; size_t l_size = 0; #if defined (HAVE_SHADOW_H) && defined (HAVE_GETSPNAM) && !defined(UNUSED_SHADOW) struct spwd * scalife; #endif struct passwd * calife; /* * become root again */ GET_ROOT; /* * returns long usually */ #ifdef HAVE_WORKING_SYSCONF l_size = (size_t) sysconf (_SC_LINE_MAX); #else l_size = MAX_STRING; #endif /* HAVE_WORKING_SYSCONF */ calife = getpwnam (name); /* null or locked password */ if (calife == NULL) die (1, "Bad pw data at line %d", __LINE__); #if defined (HAVE_SHADOW_H) && defined (HAVE_GETSPNAM) && !defined(UNUSED_SHADOW) scalife = getspnam (name); /* null or locked password */ if (scalife) { calife->pw_passwd = (char *) xalloc (strlen (scalife->sp_pwdp) + 1); strcpy (calife->pw_passwd, scalife->sp_pwdp); } #endif /* HAVE_SHADOW_H */ if ((*(calife->pw_passwd)) == '\0' || (*(calife->pw_passwd)) == '*') { #ifndef NO_SYSLOG syslog (LOG_AUTH | LOG_ERR, "NULL CALIFE %s to %s on %s", name, user_to_be, tty); closelog (); #else /* NO_SYSLOG */ if (logfile) { fprintf (logfile, "USER=%-9sTTY=%s\tDATE=%s NAME=%s << NULL PASSWORD >>\n", name, tty, this_time, user_to_be); fflush (logfile); fclose (logfile); } #endif /* NO_SYSLOG */ die (10, "Sorry.\n"); } #ifndef RELAXED if (getuid () != 0) { char got_pass = 0; char * pt_pass, * pt_enc, * user_pass, * enc_pass, salt [10]; user_pass = (char *) xalloc (l_size); enc_pass = (char *) xalloc (l_size); /* * cope with MD5 based crypt(3) */ if (!strncmp (calife->pw_passwd, "$1$", 3)) /* MD5 */ { char * pp = (char *) xalloc (strlen (calife->pw_passwd) + 1); char * md5_salt; char * md5_pass; strcpy (pp, calife->pw_passwd + 3); md5_salt = strtok (pp, "$"); md5_pass = strtok (NULL, "$"); if (md5_pass == NULL || md5_salt == NULL || (strlen (md5_salt) > 8)) /* garbled password */ { syslog (LOG_AUTH | LOG_ERR, "GARBLED PASSWORD %s to unknown %s on %s", name, user_to_be, tty); die (8, "Bad password string.\n"); } MESSAGE_1 ("MD5 password found, salt=%s\n", md5_salt); strcpy (salt, md5_salt); free (pp); } else { strncpy (salt, calife->pw_passwd, 2); salt [2] = '\0'; } for ( i = 0; i < 3; i ++ ) { pt_pass = (char *) getpass ("Password:"); /* * XXX don't assume getpass(3) will check the buffer * length. Linux glibc apparently lacks such checking and * will happily segfault if the previous entered password * was too big. * cf. */ if (pt_pass == NULL) die(1, "Corrupted or too long password"); memset (user_pass, '\0', l_size); /* * Be a bit more careful there */ strncpy (user_pass, pt_pass, l_size); user_pass[l_size - 1] = '\0'; pt_enc = (char *) crypt (user_pass, calife->pw_passwd); memset (enc_pass, '\0', l_size); strcpy (enc_pass, pt_enc); /* * assumes standard crypt(3) */ if (!strcmp (enc_pass, calife->pw_passwd)) { got_pass = 1; break; } } if (!got_pass) { #ifndef NO_SYSLOG syslog (LOG_AUTH | LOG_ERR, "BAD CALIFE %s to %s on %s", name, user_to_be, tty); closelog (); #else /* NO_SYSLOG */ if (logfile) { fprintf (logfile, "USER=%-9sTTY=%s\tDATE=%s NAME=%s << BAD PASSWORD >>\n", name, tty, this_time, user_to_be); fflush (logfile); fclose (logfile); } #endif /* NO_SYSLOG */ fprintf (stderr, "Sorry.\n"); exit (9); } free (user_pass); free (enc_pass); } #endif /* RELAXED */ /* * stay non root for a time */ RELEASE_ROOT; }