/* pGina PAM Server - A PAM-Aware Unix Daemon for pGina Copyright (C) 2003 Nathan Yocom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Email: nate.yocom@xpasystems.com Web: http://pgina.xpasystems.com Snail Mail: Nathan Yocom 9 Evergreen Farms Rd. Scarborough, ME 04074 Phone: 207-450-4948 */ /* $Log: pgina_pam_server.c,v $ Revision 1.2 2003/09/12 12:32:46 nyocom Removed unused variable in access.c Cleaned up some extranious code in actions.c Fixed 'too many file descriptors' open bug, thanks to Joseph Tombrello for the diff. Revision 1.1 2003/08/06 04:58:35 nyocom Initial Import Revision 1.26 2003/06/01 12:25:11 nyocom Changed forking model to a post-fork (1:1 per connection) Revision 1.25 2003/05/16 01:52:18 jkim -with-authasst-group option added header dependency problem in compilation fixed in pgina_pam_misc.h importing authasst.groups config file Revision 1.24 2003/05/07 21:49:16 jkim Reorganized some header dependencies Moved some misc function to pgina_pam_misc.c Changed syslog to debug_out in authasst code Reformatted usage info to fit in 80 cols. Revision 1.23 2003/05/05 08:38:57 jkim fixed code for -r -s and -prefork to not segfault fixed return code issues with arg_parse() Revision 1.22 2003/05/04 03:30:53 xpasys Bugfix for -strip-suffixes code. Revision 1.21 2003/05/01 19:23:33 xpasys Moved -prefork explanation out of authasst block Revision 1.20 2003/05/01 17:19:13 xpasys Added visual seperation in authasst options (extra tab) Revision 1.19 2003/05/01 03:47:49 xpasys fixed protocol problem with verify Revision 1.18 2003/05/01 02:40:27 xpasys Changed authasst to work with IP/FQDN/hostname. Imported source of Jiho's client into CVS Revision 1.17 2003/04/29 16:11:46 xpasys added authasst code changed argument parsing move some of main() to parent_loop() added prefork num option initial commit of authasst.c authasst.h linkedlist.c linkedlist.h. initial commit of authasst.conf files initial commit of Jiho's client test code. Revision 1.16 2003/04/28 18:50:38 xpasys Drastic changes in move to fork model Prepared for adding auth_asst code Memory management enhancements Revision 1.15 2003/04/17 17:56:11 xpasys Added debug output to read_string() Revision 1.14 2003/04/17 03:59:00 xpasys Memory audit completed, found 1 leak - patched. Revision 1.13 2003/04/17 02:40:57 xpasys Added errors to be printed to stderr Fixed exit on bad connections bug Revision 1.12 2003/04/16 06:12:57 xpasys This should be the last of the solaris support Revision 1.11 2003/04/16 05:38:56 xpasys Added -pthread and -D_REENTRANT to CFLAGS Added debug_out code (enabled with ./configure --enable-debug) Fixed potential memory issues with call to do_authenticate Better malloc/free handling Actually responds with an error to a bad password change request now Revision 1.10 2003/04/11 04:50:26 xpasys Refitted mutexing to allow for better performance, possible solve deadlock issues Added debug trace info Revision 1.9 2003/04/10 06:57:02 xpasys More solaris x86 changes Revision 1.8 2003/04/10 06:55:28 xpasys WOrkin on a fix for x86 solaris Revision 1.7 2003/04/07 17:18:26 xpasys Changed line endings to Unix Added code to be a bit friendlier on bad arguments (thanks to Jiho Kim) Revision 1.6 2003/04/05 03:35:10 xpasys Fixed segfault error in arg passing. Oy.. I hate off-by-ones ;) Revision 1.5 2003/04/05 03:18:22 xpasys Added catch for extraneous messages (i.e. message flood) attack. Revision 1.4 2003/04/04 07:58:00 xpasys Made read_string() non_blocking, times out after some time Added -l for login service name Added -a for password service name Added -r for number of retries Added -s for sleep time (usecs) General security and stability improvements Revision 1.3 2003/04/03 04:42:45 xpasys Added better memory handling Added pthread_exit() instead of null returns Removed egads dependancy Revision 1.2 2003/03/22 06:06:07 xpasys Changed to continue operation if seed_prng fails. This isn't safe, but some systems simply don't seed well apparently. Revision 1.1.1.1 2003/03/22 06:02:20 xpasys Imported sources */ #include #include #include #include #include "pgina_pam_server.h" #include "access.h" #include "actions.h" void init_globals() { port = strdup("299"); certfile = strdup("/usr/local/etc/pgina_pam/cert.pem"); login_service = strdup("login"); password_service = strdup("password"); allowfile=strdup("/usr/local/etc/pgina_pam/authasst.allow"); denyfile=strdup("/usr/local/etc/pgina_pam/authasst.deny"); hostfile=strdup("/usr/local/etc/pgina_pam/authasst.hosts"); userfile=strdup("/usr/local/etc/pgina_pam/authasst.users"); groupfile=strdup("/usr/local/etc/pgina_pam/authasst.groups"); sleep_time = 1000; // 1 millisecond, 1000 microseconds num_retries = 1000; // retries up to 1000 times num_pre_fork=5; dns_suffixes = new_LL(); //boolean with_authasst = 0; with_authasst_group = 0; } void cleanup_global() { free(port); free(certfile); free(login_service); free(password_service); free(allowfile); free(denyfile); free(hostfile); free(userfile); free(groupfile); freelist(dns_suffixes); } void usage_out(const char * argv0) { printf("Usage:\n"); printf("%s [options]\n",argv0); printf("%s -h\t(for help)\n",argv0); printf("%s -v\t(to display version information)\n",argv0); printf("\nOptions:\n"); printf(" -p [num] port to listen on\n"); printf(" -c [filename] path to the PEM certificate\n"); printf(" -r number of read retries if a client times out, default: 1000\n"); printf(" -s [num] number of microseconds to pause between reads, default 1000\n"); printf(" -l [service name] name of the auth PAM service to use, default \"login\"\n"); printf(" -a [service name] name of the password PAM service to use, default \"password\"\n"); // printf(" -loglevel [level] level=(ERR | WARNING | NOTICE | INFO | DEBUG)\n"); printf(" -prefork number of processes to prefork to listen to connections, defaults 5\n"); printf(" -with-authasst enables the authasst code\n"); printf(" -allow [filename] to read for authasst allow rules.\n"); printf(" -deny [filename] to read for authasst deny rules.\n"); printf(" -hostgroups [filename] to read for authasst hostgroup definitions.\n"); printf(" -usergroups [filename] to read for authasst usergroup definitions.\n"); printf(" -groups [filename] to read for authasst group definitions.\n"); printf(" -strip-suffixes [list] comma delimited list of DNS suffixes to strip off\n"); printf(" -strip-suffix [list] synonym for -strip-suffixes\n"); printf(" -with-authasst-group enables the authasst group behavior (implies -with-authasst)\n"); } // Modified by JK to make the code for command line option //parsing easier to look at. str and x are passed in by c-style //references int one_arg_option(char** str, int* x, const int argc, char * argv[], const char *error_message) { (*x)++; if(*x= 10) { syslog(LOG_ERR,"Client has hit threshold for bad messages, aborting connection."); debug_out("Bad message threshold hit, closing thread"); if(c_message) { free(c_message); c_message = NULL; } SSL_shutdown(serverSSL); SSL_free(serverSSL); exit(-1); } } } } while(c_message[0] != DONE); if(c_message) { free(c_message); c_message = NULL; } SSL_shutdown(serverSSL); SSL_free(serverSSL); debug_out("Closing thread and exiting"); exit(0); } int main(int argc, char *argv[]) { /*BIO *acc; SSL_CTX *ctx;*/ int serverFd = 0; struct sockaddr_in server; debug_out("[Main] Starting server."); signal(SIGPIPE,SIG_IGN); signal(SIGCHLD,SIG_DFL); // Open our syslog channel openlog("pgina_pam",LOG_CONS | LOG_PID, LOG_DAEMON); syslog(LOG_INFO,"Starting %s",VERSION); if(arg_parse(argc, argv)<=0) { //argumenting parsing error. syslog(LOG_ERR,"Unrecognized or invalid arguments. Aborting."); return -1; //arbitrary nonzero number } else { printf("%s",VERSION); } syslog(LOG_INFO,"Using port: %s and certificate: %s",port,certfile); OpenSSL_add_all_algorithms(); // Initialize the OpenSSL library SSL_load_error_strings(); // Have the OpenSSL library load its error strings syslog(LOG_INFO,"Loading entropy, this may take a minute."); if(!seed_prng(2048)) // Seed OpenSSL's PRNG syslog(LOG_INFO,"Error loading entropy - continuing anyway."); ctx = setup_ssl_ctx(certfile); if(!ctx) { syslog(LOG_ERR,"Error loading certificate or private key: %s", certfile); fprintf(stderr,"Error loading certificate or private key: %s\n", certfile); exit(-1); } // Get our file descriptor setup, then let parent_loop handle connections serverFd = socket(PF_INET, SOCK_STREAM, 0); // Setup our socket server.sin_family = AF_INET; server.sin_port = htons(atol(port)); server.sin_addr.s_addr = INADDR_ANY; if(bind(serverFd, (struct sockaddr *)&server, sizeof(server)) != 0) { debug_out("bind returned an error."); fprintf(stderr,"Error binding to port. Someone already listening?\n"); exit(1); } listen(serverFd, 30); become_daemon(); // Daemonize syslog(LOG_INFO,"Setup and initialization successful. Waiting for clients."); debug_out("[Main] Setup and loaded, waiting for clients"); //parent_loop(ctx,acc); parent_loop(serverFd); return 0; } //void parent_loop(SSL_CTX *ctx, BIO *acc) void parent_loop(int serverFd) { int clientFd = 0; int pid = 0; struct sockaddr_in client; int client_size; for(;;) { // Accept new connection, kickoff process client_size = sizeof(client); bzero(&client,sizeof(client)); clientFd = accept(serverFd, (struct sockaddr *)&client, (socklen_t *)&client_size); // This blocks pid=fork(); if(pid==0) { // Get rid of our reference to the servers file descriptor close(serverFd); child_loop(clientFd); } else { // Get rid of our reference to the clients file descriptor close(clientFd); waitpid(pid,NULL,0); } } } //void child_loop(SSL_CTX *ctx, BIO *acc) void child_loop(int clientFd) { //BIO *client; SSL *ssl; struct sockaddr_in addr; int sizeof_addr = 0; char *ipAddrStr = NULL; char *ipAddress = NULL; debug_out("in child loop"); // Daemonize ourself become_daemon(); // Now we are a child process.. eat all the mem we want ;) if(!(ssl = SSL_new(ctx))) { debug_out("[child_loop] Error creating SSL context"); syslog(LOG_ERR,"Error creating SSL context"); } // Get the clients IP address sizeof_addr = sizeof(addr); getpeername(clientFd, (struct sockaddr *) &addr, &sizeof_addr); ipAddrStr = inet_ntoa(addr.sin_addr); ipAddress = (char *) malloc(strlen(ipAddrStr) + 1); strcpy(ipAddress,ipAddrStr); debug_out("Child detached and started, New connection made from %s.",ipAddress); if(ipAddress != NULL) // Reclaim that memory! { free(ipAddress); ipAddress = NULL; } debug_out("Setting fd.."); SSL_set_fd(ssl,clientFd); debug_out("Entering server loop"); server_loop(ssl); exit(0); // again, to be safe }