Mercurial > noffle
view src/authenticate.c @ 500:614a3177b15c noffle tip
Add mail-from option.
Some modern mail systems will try and ensure the sender email is a legitimate
address. Which will fail if there isn't such an address.
author | Jim Hague <jim.hague@acm.org> |
---|---|
date | Wed, 14 Aug 2013 12:04:39 +0100 |
parents | 20abd71918ad |
children |
line wrap: on
line source
/* authenticate.c Do client authentication $Id: authenticate.c 622 2004-03-16 10:24:18Z bears $ */ #if HAVE_CONFIG_H #include <config.h> #endif #include <errno.h> #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <grp.h> #include <pwd.h> #include "common.h" #include "authenticate.h" #include "configfile.h" #include "log.h" #include "portable.h" #include "util.h" #if USE_AUTH #if USE_PAM #include <security/pam_appl.h> static const char *password; /* * It's a bit tricky to go around asking PAM questions at this stage, * as well as not fitting NNTP, so just repond to all PAM questions * with the password and hope that works. */ static int noffle_conv( int num_msg, const struct pam_message **msgm, struct pam_response **response, void *appdata_ptr ) { struct pam_response *reply; UNUSED(appdata_ptr); UNUSED(msgm); reply = calloc( num_msg, sizeof (struct pam_response) ); reply->resp = strdup( password ); reply->resp_retcode = 0; *response = reply; return PAM_SUCCESS; } static struct pam_conv conv = { noffle_conv, NULL }; static pam_handle_t *pamh = NULL; static Bool pam_session_opened = FALSE; static Bool pam_set_cred = FALSE; static uid_t oldEuid; static Bool PAM_open( void ) { int retval; /* To use PAM successfully we need to be root. */ ASSERT ( getuid() == 0 ); ASSERT( pamh == NULL ); /* * Preserve old eUid to be restored when PAM closes and set * current euid to root for PAMs benefit. */ oldEuid = geteuid(); if ( seteuid( 0 ) < 0 ) { Log_err( "Cannot set euid to root: %s", strerror( errno ) ); return FALSE; } retval = pam_start( "noffle", NULL, &conv, &pamh ); if ( retval != PAM_SUCCESS ) { Log_err( "Cannot starting authentication: %s", pam_strerror( pamh, retval ) ); return FALSE; } return TRUE; } static enum AuthResult PAM_authenticate( const char *user, const char *pass ) { int retval; ASSERT( pamh != NULL ); password = pass; retval = pam_set_item( pamh, PAM_USER, user ); if ( retval != PAM_SUCCESS ) Log_dbg( LOG_DBG_AUTH, "pam_set_item failed: %s", pam_strerror( pamh, retval ) ); if ( retval == PAM_SUCCESS ) { retval = pam_authenticate( pamh, PAM_SILENT ); if ( retval != PAM_SUCCESS ) Log_dbg( LOG_DBG_AUTH, "pam_authenticate failed: %s", pam_strerror( pamh, retval ) ); } if ( retval == PAM_SUCCESS ) { retval = pam_setcred( pamh, PAM_ESTABLISH_CRED ); if ( retval != PAM_SUCCESS ) Log_dbg( LOG_DBG_AUTH, "pam_setcred failed: %s", pam_strerror( pamh, retval ) ); else pam_set_cred = TRUE; } if ( retval == PAM_SUCCESS ) { retval = pam_open_session( pamh, 0 ); if ( retval != PAM_SUCCESS ) Log_dbg( LOG_DBG_AUTH, "pam_open_session failed: %s", pam_strerror( pamh, retval ) ); else pam_session_opened = TRUE; } switch ( retval ) { case PAM_SUCCESS: return AUTH_OK; case PAM_MAXTRIES: return AUTH_DISCONNECT; case PAM_ABORT: return AUTH_ERROR; } return AUTH_FAILED; } static void PAM_close( void ) { int retval = 0; ASSERT ( pamh != NULL ); if ( pam_session_opened ) { pam_session_opened = FALSE; retval = pam_close_session( pamh, 0 ); if ( retval != PAM_SUCCESS ) Log_dbg( LOG_DBG_AUTH, "pam_close_session failed: %s", pam_strerror( pamh, retval ) ); } if ( pam_set_cred ) { pam_set_cred = FALSE; retval = pam_setcred( pamh, PAM_DELETE_CRED ); if ( retval != PAM_SUCCESS ) Log_dbg( LOG_DBG_AUTH, "pam_set_cred failed: %s", pam_strerror( pamh, retval ) ); } retval = pam_end( pamh, retval ); if ( retval != PAM_SUCCESS ) Log_dbg( LOG_DBG_AUTH, "pam_end failed: %s", pam_strerror( pamh, retval ) ); pamh = NULL; /* * For completeness set euid back to original value, though it'll * probably be set again by Auth_dropPrivs. */ if ( seteuid( oldEuid ) < 0 ) Log_err( "Cannot set euid back to %d: %s", oldEuid, strerror( errno ) ); } #else /* * No PAM, so provide a simple alternative. * * USERSFILE is a simple plain-text file consisting of username password * pairs, one pair per line. Comments are prefixed by '#'. Blank lines * are ignored. * * By way of a simple security check, the users file MUST be only * readable and writable by the owner. */ #define AUTH_MAX_TRIES 3 static int authTries = 0; static enum AuthResult file_authenticate( const char *user, const char *pass ) { Str file, line; FILE *f; struct stat statBuf; enum AuthResult res = AUTH_FAILED; Utl_cpyStr( file, USERSFILE ); if ( stat( file, &statBuf ) < 0 ) { Log_err( "Cannot read %s (%s)", file, strerror( errno ) ); return AUTH_ERROR; } if ( !S_ISREG( statBuf.st_mode ) ) { Log_err( "%s must be a regular file, not a link", file ); return AUTH_ERROR; } if ( ( statBuf.st_mode & ( S_IRWXG | S_IRWXO ) ) != 0 ) { Log_err( "%s must be readable only by its owner", file ); return AUTH_ERROR; } if ( ! ( f = fopen( file, "r" ) ) ) { Log_err( "Cannot read %s (%s)", file, strerror( errno ) ); return AUTH_ERROR; } while ( res == AUTH_FAILED && fgets( line, MAXCHAR, f ) ) { Str theUser, thePass; char *p; p = Utl_stripWhiteSpace( line ); Utl_stripComment( p ); if ( *p == '\0' ) continue; if ( sscanf( p, MAXCHAR_FMT " " MAXCHAR_FMT, theUser, thePass ) != 2 ) { res = AUTH_ERROR; Log_err( "Badly formatted line %s in %s", p, file ); break; } if ( strcmp( user, theUser ) == 0 ) { if ( strcmp( pass, thePass ) == 0 ) res = AUTH_OK; break; } } fclose( f ); if ( res == AUTH_FAILED ) { authTries++; sleep( authTries * authTries ); if ( authTries >= AUTH_MAX_TRIES ) res = AUTH_DISCONNECT; } return res; } #endif /* USE_PAM */ #endif /* USE_AUTH */ /* Open authentication session. */ Bool Auth_open( void ) { #if USE_AUTH #if USE_PAM return PAM_open(); #else return TRUE; #endif #else return TRUE; #endif } /* Authenticate a user and password. */ enum AuthResult Auth_authenticate( const char *user, const char *pass ) { #if USE_AUTH #if USE_PAM return PAM_authenticate( user, pass ); #else return file_authenticate( user, pass ); #endif #else UNUSED(user); UNUSED(pass); return TRUE; #endif } /* Authentication session now closed. */ void Auth_close( void ) { #if USE_AUTH && USE_PAM PAM_close(); #endif } static uid_t noffleUid = (uid_t) -1; static gid_t noffleGid= (gid_t) -1; static Bool adminUser = FALSE; /* Check we have appropriate privs for authentication. */ Bool Auth_checkPrivs( void ) { uid_t euid; gid_t egid; uid_t ruid; struct passwd* pwnam; struct group* grnam; euid = geteuid(); egid = getegid(); pwnam = getpwnam( Cfg_noffleUser() ); if ( pwnam == NULL ) { Log_err( "Noffle user %s is not a known user", Cfg_noffleUser() ); return FALSE; } noffleUid = pwnam->pw_uid; grnam = getgrnam( Cfg_noffleGroup() ); if ( grnam == NULL ) { Log_err( "Noffle group %s is not a known group", Cfg_noffleGroup() ); return FALSE; } noffleGid = grnam->gr_gid; ruid = getuid(); /* Determine if admin user - root, news... */ adminUser = ( ruid == 0 || ruid == noffleUid ); if ( ! adminUser && grnam->gr_mem != NULL ) { /* ... or member of group news. */ pwnam = getpwuid( ruid ); if ( pwnam != NULL ) { char* name = pwnam->pw_name; char** grpmembers = grnam->gr_mem; char* grpmember; for ( grpmember = *grpmembers; grpmember != NULL; grpmember = *++grpmembers ) { if ( strcmp( name, grpmember ) == 0 ) { adminUser = TRUE; break; } } } else Log_err( "Cannot get user info for uid %d: %s", ruid, strerror( errno ) ); } /* * If we're really root, we will set the privs we require later. Otherwise * we need to check that everything is as it should be. */ if ( ruid != 0 ) { #if USE_AUTH && USE_PAM if( Cfg_needClientAuth() ) { Log_err( "Noffle must run as root to use PAM authentication" ); return FALSE; } #endif if ( noffleUid != euid ) { Log_err( "Noffle needs to run as root or user %s", Cfg_noffleUser() ); return FALSE; } if ( noffleGid != egid ) { Log_err( "Noffle needs to run as root or as group %s", Cfg_noffleGroup() ); return FALSE; } } return TRUE; } /* * See if we should be permitted admin access. Admins can do anything, * non-admins can only read articles, list groups and post. * * This must be called after Auth_checkPrivs. */ Bool Auth_admin( void ) { ASSERT( noffleUid != (uid_t) -1 && noffleGid != (gid_t) -1 ); return adminUser; } /* * Drop any privs required for authentication. * * Must be called AFTER Auth_checkPrivs. */ Bool Auth_dropPrivs( void ) { uid_t euid; ASSERT( noffleUid != (uid_t) -1 && noffleGid != (gid_t) -1 ); /* * We only need to drop privs if we're currently root. We * should have already checked we're the news user on startup. */ euid = geteuid(); if ( euid != 0 ) return TRUE; if ( setgid( noffleGid ) != 0 ) { Log_err( "Can't set group %s: %s", Cfg_noffleGroup(), strerror( errno ) ); return FALSE; } if ( setuid( noffleUid ) != 0 ) { Log_err( "Can't set user to %s: %s", Cfg_noffleUser(), strerror( errno ) ); return FALSE; } return TRUE; }