diff src/server.c @ 288:c02c4eb95f95 noffle

[svn] * src/configfile.h,src/configfile.c,docs/noffle.conf.5: Add noffle-user and noffle-group configs. * src/configfile.c,src/fetch.c,src/fetchlist.c,src/protocol.c, src/server.c: Replace strcpy() with Utl_cpyStr() where appropriate. See Debian bug 168128. * src/control.c,src/configfile.c,src/noffle.c: Replace [s]scanf("%s") with [s]scanf(MAXCHAR_FMT). * src/noffle.c: Log warning if noffle.conf is world readable. * src/noffle.c: Restrict most options to news admins; i.e. those who are root or news on running Noffle. * Makefile.in,acconfig.h,aclocal.m4,config.h.in,configure,configure.in, docs/Makefile.in,docs/noffle.conf.5,packages/Makefile.in, packages/redhat/Makefile.in,src/Makefile.am,src/Makefile.in, src/authenticate.c,src/authenticate.h,src/noffle.c,src/server.c: Add basic authentication using either Noffle-specific user file or authenticating via PAM (service 'noffle'). PAM authentication needs to run as root, so a Noffle server that needs PAM must be started by root. Helpful (?) error messages will be logged if not. Noffle will switch ruid and euid to 'news' (or whatever is configured) ASAP. * src/noffle.c: Add uid checking.
author bears
date Fri, 10 Jan 2003 23:25:45 +0000
parents 3477050e8d10
children f81fdcc2696b
line wrap: on
line diff
--- a/src/server.c	Fri Jan 10 23:11:43 2003 +0000
+++ b/src/server.c	Fri Jan 10 23:25:45 2003 +0000
@@ -1,7 +1,7 @@
 /*
   server.c
 
-  $Id: server.c 403 2002-11-10 11:32:17Z bears $
+  $Id: server.c 420 2003-01-10 23:25:45Z bears $
 */
 
 #if HAVE_CONFIG_H
@@ -26,6 +26,7 @@
 #include <stdarg.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include "authenticate.h"
 #include "client.h"
 #include "common.h"
 #include "configfile.h"
@@ -49,6 +50,12 @@
 #include "wildmat.h"
 #include "portable.h"
 
+enum AuthState {
+    NEED_USER,
+    NEED_PASS,
+    AUTH_DONE
+};
+
 struct
 {
     Bool running;
@@ -58,18 +65,32 @@
     DynStr *reply;
     Bool eotAfterReply;
     Bool groupReady;
-} server = { FALSE, 0L, 0, "", NULL, FALSE, FALSE };
+    enum AuthState auth;
+    char *user;
+} server = {
+    FALSE,		/* running */
+    0L,			/* lastServerOpen */
+    0,			/* artPtr */
+    "",			/* grp */
+    NULL,		/* reply */
+    FALSE,		/* eotAfterReply */
+    FALSE,		/* groupReady */
+    NEED_USER,		/* auth */
+    NULL		/* user */
+};
 
 typedef struct Cmd
 {
     const char *name;
     const char *syntax;
+    Bool needAuth;
     /* Returns false, if quit cmd */
     Bool (*cmdProc)( char *arg, const struct Cmd *cmd );
 }
 Cmd;
 
 static Bool doArt( char *arg, const Cmd *cmd );
+static Bool doAuthinfo( char *arg, const Cmd *cmd );
 static Bool doBody( char *arg, const Cmd *cmd );
 static Bool doGrp( char *arg, const Cmd *cmd );
 static Bool doHead( char *arg, const Cmd *cmd );
@@ -96,27 +117,28 @@
 
 Cmd commands[] =
 {
-    { "article", "ARTICLE [msg-id|n]", &doArt },
-    { "body", "BODY [msg-id|n]", &doBody },
-    { "head", "HEAD [msg-id|n]", &doHead },
-    { "group", "GROUP grp", &doGrp },
-    { "help", "HELP", &doHelp },
-    { "ihave", "IHAVE (ignored)", &doIhave },
-    { "last", "LAST", &doLast },
+    { "article", "ARTICLE [msg-id|n]", TRUE, &doArt },
+    { "authinfo", "AUTHINFO USER username|PASS password", FALSE, &doAuthinfo },
+    { "body", "BODY [msg-id|n]", TRUE, &doBody },
+    { "head", "HEAD [msg-id|n]", TRUE, &doHead },
+    { "group", "GROUP grp", TRUE, &doGrp },
+    { "help", "HELP", FALSE, &doHelp },
+    { "ihave", "IHAVE (ignored)", TRUE, &doIhave },
+    { "last", "LAST", TRUE, &doLast },
     { "list", "LIST [ACTIVE [pat]]|ACTIVE.TIMES [pat]|"
-      "EXTENSIONS|NEWSGROUPS [pat]|OVERVIEW.FMT", &doList },
-    { "listgroup", "LISTGROUP grp", &doListgrp },
-    { "mode", "MODE (ignored)", &doMode },
-    { "newgroups", "NEWGROUPS [xx]yymmdd hhmmss [GMT]", &doNewgrps },
-    { "newnews", "NEWNEWS (not implemented)", &notImplemented },
-    { "next", "NEXT", &doNext },
-    { "post", "POST", &doPost },
-    { "quit", "QUIT", &doQuit },
-    { "slave", "SLAVE (ignored)", &doSlave },
-    { "stat", "STAT [msg-id|n]", &doStat },
-    { "xhdr", "XHDR over-field [msg-id|m[-[n]]]", &doXhdr },
-    { "xpat", "XPAT over-field msg-id|m[-[n]] pat", &doXpat },
-    { "xover", "XOVER [m[-[n]]]", &doXOver }
+      "EXTENSIONS|NEWSGROUPS [pat]|OVERVIEW.FMT", TRUE, &doList },
+    { "listgroup", "LISTGROUP grp", TRUE, &doListgrp },
+    { "mode", "MODE (ignored)", FALSE, &doMode },
+    { "newgroups", "NEWGROUPS [xx]yymmdd hhmmss [GMT]", TRUE, &doNewgrps },
+    { "newnews", "NEWNEWS (not implemented)", TRUE, &notImplemented },
+    { "next", "NEXT", TRUE, &doNext },
+    { "post", "POST", TRUE, &doPost },
+    { "quit", "QUIT", FALSE, &doQuit },
+    { "slave", "SLAVE (ignored)", TRUE, &doSlave },
+    { "stat", "STAT [msg-id|n]", TRUE, &doStat },
+    { "xhdr", "XHDR over-field [msg-id|m[-[n]]]", TRUE, &doXhdr },
+    { "xpat", "XPAT over-field msg-id|m[-[n]] pat", TRUE, &doXpat },
+    { "xover", "XOVER [m[-[n]]]", TRUE, &doXOver }
 };
 
 /*
@@ -664,6 +686,102 @@
 }
 
 static Bool
+doAuthinfo( char *line, const Cmd *cmd )
+{
+#if USE_AUTH
+    Str s, arg;
+    const char *data;
+
+    if ( ! Cfg_needClientAuth() )
+    {
+	putStat( STAT_AUTH_REJECTED, "Authentication not required" );
+	return TRUE;
+    }
+
+    if ( sscanf( line, MAXCHAR_FMT, s ) != 1 )
+    {
+    badsyntax:	
+	if ( server.auth != AUTH_DONE )
+	    server.auth = NEED_USER;
+	putSyntax( cmd );
+    }
+    else
+    {
+	Utl_toLower( s );
+	Utl_cpyStr( arg, Utl_restOfLn( line, 1 ) );
+	data = Utl_stripWhiteSpace( arg );
+
+	if ( strcmp( "user", s ) != 0 && strcmp( "pass", s ) != 0 )
+	    goto badsyntax;
+	
+	if ( strcmp( "user", s ) == 0 && server.auth == NEED_USER )
+	{
+	    if ( server.user != NULL )
+		free( server.user );
+	    Utl_allocAndCpy( &server.user, data );
+	    server.auth = NEED_PASS;
+	    putStat( STAT_MORE_AUTH_REQUIRED, "More authentication required" );
+	}
+	else if ( strcmp( "pass", s ) == 0 && server.auth == NEED_PASS )
+	{
+	    enum AuthResult authRes = Auth_authenticate( server.user, data );
+	    char *p;
+
+	    /* Zap the password */
+	    for ( p = line; *p != '\0'; p++ )
+		*p = 'X';
+	    
+	    switch ( authRes )
+	    {
+	    case AUTH_OK:
+		server.auth = AUTH_DONE;
+		putStat( STAT_AUTH_ACCEPTED, "Authentication accepted" );
+		Log_inf( "User %s authenticated", server.user );
+		break;
+
+	    case AUTH_FAILED:
+		server.auth = NEED_USER;
+		putStat( STAT_NO_PERMISSION, "No permission");
+		Log_dbg( LOG_DBG_AUTH, "User %s password %s rejected",
+			 server.user, data );
+		break;
+
+	    case AUTH_DISCONNECT:
+		putStat( STAT_NO_PERMISSION, "No permission - disconnecting" );
+		Log_dbg( LOG_DBG_AUTH,
+			 "User %s password %s rejected - disconnecting",
+			 server.user, data );
+		return FALSE;
+		
+	    default:	/* AUTH_ERROR */
+		putStat( STAT_PROGRAM_FAULT, "Authentication program error" );
+		Log_dbg( LOG_DBG_AUTH,
+			 "Error authenticating User %s password %s",
+			 server.user, data );
+		return FALSE;
+	    }
+	}
+	else
+	{
+	    if ( server.auth == AUTH_DONE )
+		putStat( STAT_AUTH_REJECTED, "Reauthentication not possible" );
+	    else
+	    {
+		putStat( STAT_AUTH_REJECTED, "Authentication rejected" );
+		server.auth = NEED_USER;
+	    }
+	}
+    }
+#else
+    UNUSED( line );
+    UNUSED( cmd );
+    
+    putStat( STAT_AUTH_REJECTED, "Authentication not possible" );
+#endif    
+    return TRUE;
+}
+
+static Bool
 doHelp( char *arg, const Cmd *cmd )
 {
     unsigned int i;
@@ -839,7 +957,7 @@
     else
     {
         Utl_toLower( s );
-        strcpy( arg, Utl_restOfLn( line, 1 ) );
+        Utl_cpyStr( arg, Utl_restOfLn( line, 1 ) );
         pat = Utl_stripWhiteSpace( arg );
         if ( pat[ 0 ] == '\0' )
             pat = "*";
@@ -1437,7 +1555,7 @@
 
 /* Parse line, execute command and return FALSE, if it was the quit command. */
 static Bool
-parseAndExecute( Str line )
+parseAndExecute( Str line, Bool authDone )
 {
     unsigned int i, n;
     Cmd *c;
@@ -1447,11 +1565,20 @@
     if ( sscanf( line, MAXCHAR_FMT, s ) == 1 )
     {
         Utl_toLower( s );
-        strcpy( arg, Utl_restOfLn( line, 1 ) );
+        Utl_cpyStr( arg, Utl_restOfLn( line, 1 ) );
         n = sizeof( commands ) / sizeof( commands[ 0 ] );
         for ( i = 0, c = commands; i < n; ++i, ++c )
             if ( strcmp( c->name, s ) == 0 )
             {
+#if USE_AUTH
+		if ( c->needAuth && ! authDone )
+		{
+		    putStat( STAT_AUTH_REQUIRED, "Authentication required" );
+		    return TRUE;
+		}
+#else
+		UNUSED( authDone );
+#endif		     
                 ret = c->cmdProc( Utl_stripWhiteSpace( arg ), c );
                 return ret;
             }
@@ -1501,6 +1628,63 @@
     Str line;
 
     putWelcome();
+
+#if USE_AUTH
+    /*
+     * If authentication is required, we issue the welcome message and
+     * go into a command loop that doesn't open the databases and just
+     * accepts authentication commands. Once successfully authenticated,
+     * we drop root privs (if we have them - they are needed for PAM
+     * authentication to work) and proceed to the normal loop.
+     */
+    if ( Cfg_needClientAuth() )
+    {
+	if ( ! Auth_open() )
+	{
+	    initOutput();
+	    putFatal( "Cannot open authorisation" );
+	    sendOutput();
+	    return;
+	}
+
+	done = FALSE;
+	while ( ! done )
+	{
+	    if ( Prt_getLn( line, stdin, -1 ) )
+	    {
+		initOutput();
+
+		if ( ! parseAndExecute( line, FALSE ) )
+		    done = TRUE;
+
+		if ( server.auth == AUTH_DONE )
+		    done = TRUE;
+
+		sendOutput();
+	    }
+	    else
+	    {
+		Log_inf( "Client disconnected. Terminating." );
+		done = TRUE;
+	    }
+	}
+
+	Auth_close();
+
+	/* Did we finish because we successfully authenticated? */
+	if ( server.auth != AUTH_DONE )
+	    return;
+    }
+#endif
+
+    if ( ! Auth_dropPrivs() )
+    {
+	initOutput();
+	putFatal( "Cannot set user privs" );
+	sendOutput();
+	return;
+    }
+    
     done = FALSE;
     while ( ! done )
     {
@@ -1515,7 +1699,7 @@
 	    }
 	    else
 	    {
-		if ( ! parseAndExecute( line ) )
+		if ( ! parseAndExecute( line, TRUE ) )
 		    done = TRUE;
 	    }