/* ***************************************************************************** * * Copyright 1989, 1990, Xylogics, Inc. ALL RIGHTS RESERVED. * * ALL RIGHTS RESERVED. Licensed MAterial - Property of Xylogics, Inc. * This software is made available solely pursuant to the terms of a * software license agreement which governs its use. * Unauthorized duplication, distribution or sale is strictly prohibited. * * Module Description: * * Security Policy - Annex Security Server * * Original Author: Dave Harris Created on: July 1986 * * Module Reviewers: * * lint harris parker * ***************************************************************************** */ #include "comdefs.h" /* Include Files */ #include #include "../inc/port/install_dir.h" #include "../inc/erpc/acp_const.h" #include #include "../inc/config.h" #include "../inc/port/port.h" #include "radius.h" #include "acp_policy.h" #include #include #include #ifdef _WIN32 #define SECURID_CARD #define ACE2_0 #ifndef BAY_ALPHA /* Safeword not supported on DEC Alpha */ #define ENIGMA_SAFEWORD #define NET_ENIGMA_ACP #endif #include "..\ntsrc\registry\xyreg.h" #include #else /* not _WIN32 */ #include #include #include #include "erpc/nerpcd.h" #include #include #include "acp_group.h" #endif /* not _WIN32 */ #include "acp_regime.h" /*contains proto. 4 get_security_regime() */ #include "../inc/erpc/nerpcd.h" #include "acp.h" #ifdef USE_NDBM #include #include "acp_dbm_lib.h" #endif #include "getacpuser.h" #include "environment.h" #include "acp_lib.h" #include "acp_trap.h" #ifndef _WIN32 #include "session_db.h" #endif /* _WIN32 */ #ifdef _WIN32 #include "../inc/rom/syslog.h" #else /* not WIN32 */ #include #endif /* not _WIN32 */ #include #include #ifdef USESHADOW #ifdef NATIVESHADOW #include "shadow.h" #else #include "ashadow.h" #endif #endif /* external variables */ extern int deny_all_users; /* Defined in getacpuser.c * Set to true if userinfo database is corrupted * due to an invalid acp_userinfo file. All access * is denied. */ extern int acp_timer_enabled; /* Defined in acp.c If set to true, invokes the * SIGALRM and vice versa. */ extern StructErpcdOption *ErpcdOpt; /* Defined in erpcd.c Handles logging options for * acp logging. */ extern char szDefaultDomain[]; extern int alarm_flag; #ifdef KERBEROS #include #ifndef MAXPATHLEN #define MAXPATHLEN 1024 #endif #include "krb.h" #include "des.h" #define KRB_TK_DIR "/tmp/tkt_erpcd_" #endif #ifdef SECURID_CARD #ifdef ACE1_1 #include "sdcli.h" #endif /* ACE1_1 */ #if (defined(ACE1_2) || defined(ACE2_0)) && !defined(_WIN32) #include "sdi_athd.h" #include "sdi_size.h" #include "sdi_type.h" #include "sdacmvls.h" #include "sdconf.h" union config_record configure; #ifdef ACE2_0 #include "../sdclient/sdi_defs.h" #endif /*ACE2_0*/ #endif /*(ACE1_2 || ACE2_0) && not _WIN32)*/ #if defined(_WIN32) /* this vendor may be #defining const (tsk! tsk!) */ /* if so, undefine it afterwards */ #ifndef const #define APC_POLICY_C_CONST_NOT_DEFINED #endif /* not const */ #include "../sdiclient/sdathd.h" #include "../sdiclient/sdacmvls.h" #if defined(const) && defined(APC_POLICY_C_CONST_NOT_DEFINED) #undef const #endif /* defined(const) && defined(APC_POLICY_C_CONST_NOT_DEFINED) */ #endif /* _WIN32 */ #endif /* SECURID_CARD */ #if defined(ENIGMA_SAFEWORD) && !defined(_WIN32) #include "custpb.h" #include "custfail.h" #endif /* defined(ENIGMA_SAFEWORD) && !defined(_WIN32) */ #if defined(ENIGMA_SAFEWORD) && defined(_WIN32) #undef PROTOTYPING /* (buggy swecapi.h can't handle it) */ #include "../enigma/swecapi.h" #endif /* defined(ENIGMA_SAFEWORD) && defined(_WIN32) */ #include "../inc/erpc/netadmp.h" /* * Create srtings that can indicate how the build was done. This must be done * only after reading in the acp_policy.h which has the final say on * definitions. Since customers may build differently this can help the * technical support people figure out what is in the system. They can do a * "strings erpcd | grep XY" to get the information they need. */ /* The supported regimes. */ #ifdef ACPPASSWD static char *xy_def1 = "\nXY defines: ACPPASSWD\n"; #endif #ifdef NATIVEPASSWD static char *xy_def2 = "\nXY defines: NATIVEPASSWD\n"; #endif #if defined(ENIGMA_SAFEWORD) && !defined(NET_ENIGMA_ACP) static char *xy_def3 = "\nXY defines: ENIGMA_SAFEWORD\n"; #endif #ifdef SECURID_CARD static char *xy_def4 = "\nXY defines: SECURID_CARD\n"; #endif #ifdef KERBEROS static char *xy_def5 = "\nXY defines: KERBEROS\n"; #endif /* Using stub shared libraries? */ #ifdef STUB_SW static char *xy_def6 = "\nXY defines: STUB_SW\n"; #endif #ifdef STUB_SID static char *xy_def7 = "\nXY defines: STUB_SID\n"; #endif #ifdef STUB_KRB static char *xy_def8 = "\nXY defines: STUB_KRB\n"; #endif /* Variety of secureid regime */ #ifdef ACE1_1 static char *xy_def9 = "\nXY defines: ACE1_1\n"; #endif #ifdef ACE1_2 static char *xy_def10 = "\nXY defines: ACE1_2\n"; #endif #ifdef ACE2_0 static char *xy_def11 = "\nXY defines: ACE2_0\n"; #endif #ifdef PASS_SEC static char *xy_def12 = "\nXY defines: PASS_SEC\n"; #endif /* Using shadow passwds for acp and/or native regimes */ #ifdef NATIVESHADOW static char *xy_def13 = "\nXY defines: NATIVESHADOW\n"; #endif #ifdef USESHADOW static char *xy_def14 = "\nXY defines: USESHADOW\n"; #endif #ifdef NET_ENIGMA_ACP static char *xy_def15 = "\nXY defines: NET_ENIGMA_ACP\n"; #endif #define END_OF_FILE 1001 #define IS_ANNEX 1002 #define IS_ANNEX_NOT 1003 #define IS_HOST 1004 #define IS_ENV_STRING 1005 #define IS_ENV_STRING_NOT 1006 #define IS_ENV_STRING_ERR 1007 #define SIZE_INET 64 #define TOKEN_SIZE 256 /*uniform token size used by different parsers*/ #define EXCLUDE_LIST 1 #define INCLUDE_LIST 2 #define ACCESS_RESTRICTED 0 #define ACCESS_UNRESTRICTED 1 /* Defining an enum for the various code paths chosen */ #define PORT_TO_ANNEX 0 #define IPX_VALIDATE 1 #define PPP_VALIDATE 2 #define NET_TO_PORT 3 /* Function declarations */ /* allow prototype checking, but only when requested */ #ifdef _ #undef _ #endif #ifndef _ #if (__STDC__ && PROTOTYPES) #define _(x) x #else #define _(x) () #endif /* STDC */ #endif /* _ */ #ifdef USESHADOW int warn_user(); #endif int setacpdialup(), endacpdialup(); int acp_validate(); int acp_kerberos_validate(); int acp_native_validate(); int acp_acknowledge(); void log_message(); void terminate_session(); int return_dialup_address(); int return_max_logon_tcp(); int dialup_address_authorize(); int return_user_index(); int return_log(); int return_serial_validate(); int ppp_security_authorize(); int return_port_to_annex(); int promptstring_wt(); int promptstring(); int outputstring(); int port_to_annex_authorize(); int return_annex_to_net(); int inet_name(); int available(); int annex_to_net_authorize(); int return_net_to_port(); int net_to_port_authorize(); int return_appletalk_profile(); int return_hook_callback(); int return_max_links(); int create_group_list(); int acp_netsafeword_validate(); int acp_auth_resp(); int write_audit_log(); int get_string(); int wild_match(); int inet_match(); int get_token(); int acp_request_dialout_tcp(); int acp_request_dialout_udp(); char *crypt(); int get_user_profile_entry(); void release_uprof(); void acp_special(); int acp_getusr(); char *test_password(), *change_password(); static UINT32 dialup_address_validate(); static int acp_securid_authenticate(); static int acp_securid_validate(); static void acp_safeword_printmsg(); static void process_error(); static void unlock_database(); void inet_number(); UINT32 inet_address(), hash(); int check_port_pool(); void check_dialback(); void ipx_check_dialback(); void dialback_log(); int access_code_validate(); int findacpdialup(); int hook_callback(); int hook_callback_string(); int user_index_authorize(); void release_env(); void racp_destroy_strlist_chain(); UINT32 get_chap_secret(); static int generic_authenticate_user _((ACP *, UINT32 logid, char *Name, char *Pass, int prompt, int max_retries, char *Message, ARQ_PROFILE *opt_info)); static int map_mask_to_code(int); #ifdef _WIN32 int syslog( int pri, const char *format, ...); int NTValidate(); /* in ntsupport.c */ extern char *PrependDomainNameAndFix(char *src, char *dest); #endif /************************************************************************ * MACRO DEFINITIONS PRIVATE TO THIS MODULE */ /* Return values for get_host_and_ports() * FOUND_HOST - contained host entry but no port specification * FOUND_HOST_PORTS - contained both host entry and port specification * FOUND_HOST_ERROR - syntax error in token */ #define FOUND_HOST 1 #define FOUND_HOST_PORTS 2 #define FOUND_HOST_ERROR 3 #define HOST_PORT_BEGIN_SEPCH '[' #define HOST_PORT_END_SEPCH ']' /* Global data declarations */ extern char group_file[]; struct gr_file fileinfo={group_file, DEFAULT_GROUP,0}; /* used to pass group file info. where inaccessible * due to modularity issues. Used for userinfo * lookups primarily. */ /* * Un-comment this line to get debugging * information about dialup slip and ppp. */ /* #define DEBUG_DIALUP */ /* print debug statements for dialup */ int debug_dialup = #ifdef DEBUG_DIALUP 1; #else 0; #endif INT32 daysleft; /* Used to print password & account warnings */ /* External data */ extern int debug; #ifndef FREEBSD #ifndef BSDI #ifndef _WIN32 /* declared in for _WIN32 */ extern char *sys_errlist[]; #endif #endif #endif /* Used by the shadow-password-file code for aging passwords. */ static char old_password[LEN_ACCESS_CODE]; /* LEN_ACCESS_CODE == 16 */ /* Used to force a command into a user session for macro-per-user */ static char stuff_command[ACP_LONGSTRING]; /* ACP_LONGSTRING == 80 */ static char syslogbuf[512]; char unknown[] = ""; char none[] = " "; /* * Define the Annex acp encryption key/password list * (wildcard table and normal table) */ typedef struct key_list { UINT32 addr; /* internet address (number) */ KEYDATA *ckey; /* crypt key and tables */ } KEY_LST; KEY_LST Wild[MAX_WILD], Tame[MAX_TAME]; int Nwild = 0, Ntame = 0; /* number of actual entries */ /* * Keep track of allocated memory to free on SIGHUP signal */ char *Allocated[MAX_KEYS]; /* list of addresses to free */ int Nmalloced = 0; /* number of allocated KEYDATA's */ #if defined(ENIGMA_SAFEWORD) && !defined(_WIN32) static struct pblk pblock; static char savdyn[LEN_ACCESS_CODE]; /* LEN_ACCESS_CODE == 16 */ static char savcha[ACP_MAXSTRING]; /* ACP_MAXSTRING == 32 */ #ifdef USE_SYSLOG extern char *service_name[NSERVICES]; #endif /* USE_SYSLOG */ #endif /* defined(ENIGMA_SAFEWORD) && !defined(_WIN32) */ #define LINE \ puts("\n-----------------------------------------------------------------------"); /************************************************************************ * FUNCTIONS KNOWN TO BE PRIVATE TO THIS MODULE */ /************************************************************************ * get_host_and_ports * Separate a host token into hostname and port parts * IN token_p host token * OUT host_pp address of start of host part * OUT ports_pp address of start of ports part * Results: * FOUND_HOST - contained host entry but no port specification * FOUND_HOST_PORTS - contained both host entry and port specification * FOUND_HOST_ERROR - syntax error in token */ static int get_host_and_ports(token_p, host_pp, ports_pp) char *token_p; char **host_pp; char **ports_pp; { char *temp_p; /* First, set the host part equal to the beginning of * the string. */ *host_pp = token_p; /* Next, look and see if there is a beginning port separator * character. If not, we return that we saw only the host * part, after setting the port pointer to NULL. */ if((*ports_pp = strchr(token_p, HOST_PORT_BEGIN_SEPCH)) == (char *) 0) return(FOUND_HOST); /* If we got here, we're pointing at the opening bracket. * First, we replace it with a NULL and move the ports * pointer forward to point at the beginning of the * ports expression. */ *(*ports_pp)++ = '\0'; /* separates host from ports */ /* Now we need to determine whether the syntax is correct. * This can be quickly deduced as follows: * - The string is scanned for the ending separator character. * This must occur once and only once, and at the end. * - The resulting string must contain only digits, commas, * and dashes. * - Dashes or commas cannot appear at the beginning or the * end of the string ("-90", "90-", "90,", etc., are not * valid). */ /* Find the separator. It must be there, and at the end. */ if(((temp_p = strchr(*ports_pp, HOST_PORT_END_SEPCH)) == (char *) 0) || (strlen(temp_p) > 1)) return(FOUND_HOST_ERROR); else *temp_p = '\0'; /* Get rid of it */ /* Look for stray characters */ for(temp_p = *ports_pp; *temp_p; temp_p++) if(!isdigit(*temp_p) && !isspace(*temp_p) && (*temp_p != ',') && (*temp_p != '-')) break; /* Make sure that we got all the way through, and that there * are digits on both ends. */ if((*temp_p != '\0') || !isdigit(*(temp_p - 1)) || !isdigit(**ports_pp)) return(FOUND_HOST_ERROR); /* We have both a host and a ports specification. */ return(FOUND_HOST_PORTS); } /* get_host_and_ports */ /************************************************************************ * match_host_ports * Attempt to find a match between a specified port and list of ports * IN port port to match * IN portlist_p list of ports to match against * Results: TRUE if portlist_p == NULL or port in portlist_p, * FALSE otherwise. */ static int match_host_ports(port, portlist_p) int port; char *portlist_p; { char *temp_p, *walk_p; int saveport = -1; /* Save start of range */ char savech; /* First check and see if we even have to bother. If there's * no ports list, it matches by definition. */ if(portlist_p == (char *) 0) return(TRUE); /* Walk through the list, searching out valid ports one * at a time. */ temp_p = walk_p = portlist_p; while(*temp_p) { /* temp_p points to beginning of next token. * scan ahead until we find next non-digit. */ while(isdigit(*walk_p)) walk_p++; /* Save the character and NULL it out temporarily * to get the string port value. */ savech = *walk_p; *walk_p = '\0'; /* If we get a range delimiter, we need to * save this number and get the rest of it * before we can match. */ if(savech == '-') { saveport = atoi(temp_p); /* Skip ahead */ *walk_p = savech; temp_p = ++walk_p; /* And around we go again */ continue; } /* If it's any other character we need to check * and see if we got a range. */ if(saveport != -1) { /* Check to see if it's within the range. * Be pessimistic about a user's entry - * they might have said hi-lo, as opposed * to lo-hi. */ if((saveport < atoi(temp_p)) && (port >= saveport) && (port <= atoi(temp_p))) { /* Found it */ *walk_p = savech; return(TRUE); } else if((saveport >= atoi(temp_p)) && (port >= atoi(temp_p)) && (port <= saveport)) { /* Found it */ *walk_p = savech; return(TRUE); } else { /* No match here */ saveport = -1; /* Skip ahead */ *walk_p = savech; temp_p = ++walk_p; /* And go around again */ continue; } } else if(atoi(temp_p) == port) { /* Not a range. Try matching it as * a simple port number. */ *walk_p = savech; return(TRUE); } /* If we get here we've got no match. Figure * out if there's any more ports or not. */ if(savech == '\0') { /* Last one - no luck. */ return(FALSE); } /* Move beyond these and try again */ *walk_p = savech; temp_p = ++walk_p; } /* We should never get here, but just in case...*/ return(FALSE); } /* match_host_ports */ /* * Annex Security Policy Procedures * * global_init() Annex boot, or security system restart * * ppp_security() Grant or deny access to Annex PPP or SLIP port * * dialup_address() Grant or deny access to Annex dialup user. * * port_to_annex() Grant or deny access to Annex CLI * port_to_annex_logout() Log message when CLI is terminated * * annex_to_net() Allow connection from Annex to a Host * annex_to_net_logout() Log message when connection is closed * * annex_to_lat() Log LAT connection attempt * * net_to_port() Grant or deny access to a port via port server * net_to_port_logout() Log message when connection is broken * * appletalk_profile() Return per user AppleTalk information * * user_index() Return password based on username. * * up_secret() Return secret based on username * * Refer to ./policy.doc for information on customizing a security policy * See Chapter 8 of the Annex Network Administrator's Guide (Security) * * FILES: * * In the examples below, INSTALL_DIR is selected by the install-annex * script, and supplied to the erpcd makefile * * INSTALL_DIR/acp_keys Annex encryption keys, see ENCRYPTION * INSTALL_DIR/acp_passwd Annex password file, like /etc/passwd * INSTALL_DIR/acp_restrict Connectivity restrictions for annex_to_net * INSTALL_DIR/acp_dialup Annex dialup addresses file * INSTALL_DIR/acp_logfile Security Event Logging File * INSTALL_DIR/acp_user Per User Configuration File * * ENCRYPTION: * * Messages between the security server and the Annex are encrypted if * the Annex NA parameter acp_key is set, and the Annex is subsequently * booted, given the NA "reset annex security" command, or CLI superuser * "passwd" command. Encryption keys for the server are stored in the * acp_keys file. Annexes with no entries are assumed to have no key * set. Since wildcards can be used, it might be necessary to include * an entry in this file by explicitly declaring "no key", for example: * * annex01, annex02: seKret2 * # 131.21 net Annexes have same key except for 3 Annexes * 131.21.*: Gub-Net * 131.21.1.1: SpeciaL * 131.21.2.1, 131.21.2.2: * * Keys on the original Annex are limited to 7 characters, on the Annex II * 15 characters. Any alphanumeric character and most special characters * may be included in a key. */ /* * global_init() * * This interface is called when the Annex boots, and security is enabled * or if security is enabled for the first time by setting enable_security * to Y (with NA) followed by a "reset annex security" or CLI passwd * command. In the first case, the Message is the boot image; in the * latter case, the Message is "(restart)". */ void global_init(Acp, logid, inet, Message) ACP *Acp; /* Handle to pass back to library calls */ UINT32 logid, /* Log sequence number (initial) */ inet; /* Annex Internet address */ char *Message; /* image booted, or "(restart)" */ { /* Acknowledge call with a generic return message */ (void)acp_acknowledge(Acp); /* Log a message with initial sequence number and image name */ if(ISUDP(Acp->state)) log_message(inet, logid, 0, 0, SERVICE_SECURITY, EVENT_BOOT, Message); /* Exit this session */ terminate_session(); /* Return - unlikely since terminate_session() exit()s */ return; /* void */ } /* * dialup_address * * This interface is called whenever a user must secure a port for * dialup access. */ void dialup_address(Acp, logid, inet, port, ptype, sf, sr, Name, loc, rem, node, get_filters, get_routes) ACP *Acp; /* Handle to pass to library functions */ UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port, /* physical/virtual port number */ sf, /* protocol service user is on */ sr; /* Expect SERVICE_DIALUP */ ACP_USTRING Name; /* Username */ UINT32 loc,rem; /* local and remote addresses */ unsigned char *node; int get_filters, get_routes; int ptype; { int net_type; STR_LIST *filters, *routes; STR_LIST **fp, **rp; UINT32 dialup_flags; fp = rp = NULL; filters = routes = NULL; if (!(ISUDP(Acp->state))) { /* if UDP, there's can't be filters/routes */ if (get_filters) fp = &filters; if (get_routes) rp = &routes; } if (debug) printf("dialup_address: %swant filters, %swant routes\n", fp ? "" : "do not ", rp ? "" : "do not "); /* Setup port type based on service */ if (sr == SERVICE_DIALUP) { net_type = IP_ADDRT; } else if (sr == SERVICE_DIALUP_IPX) { net_type = IPX_ADDRT; } /* Send the local and remote addresses as a hint. We may or may not get the same addresses back. */ (void)return_dialup_address(Acp, REQ_PENDING, net_type, loc, rem, node, filters, routes); if(dialup_address_validate(Name, inet, net_type, &loc, &rem, node, port, ptype, fp, rp, sf, &dialup_flags)) { (void)dialup_address_authorize(Acp, dialup_flags, net_type, loc, rem, node, filters, routes); if(ISUDP(Acp->state)) log_message(inet, logid, port, ptype, sr, EVENT_PROVIDE, Name); } else { (void)dialup_address_authorize(Acp, dialup_flags, net_type, loc, rem, node, NULL, NULL); if(ISUDP(Acp->state)) log_message(inet, logid, port, ptype, sr, EVENT_NOPROVIDE, Name); } return; } /* * user_index * * This interface is called whenever a password or CHAP secret * needs to be determined based on a username. */ void user_index(Acp, logid, inet, port, ptype, service, Name, endpoint) ACP *Acp; /* Handle to pass to library functions */ UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port, /* physical/virtual port number */ ptype, /* port type */ service; /* Expect SERVICE_DOS */ ACP_USTRING Name; /* Username */ EndpDesc *endpoint; /* MP Endpoint Discriminator */ { /* * Adding the per-user code to this interface. This code will * create a user's environment and search the database using * this environment. */ Uprof up; /* storage for userinfo database */ /* entries. */ int error, n; /* returned error codes from user*/ /* -info search. */ UINT32 ret_err_code= REJECT_CODE(CODE_UNKNOWN, REJ_ERPCDDENY); /* codes identifying regime and */ /* reasons used for logging. */ struct environment_spec env, *env_p = &env; char upfield[ACP_MAXSTRING]; /* message for authorization. */ #ifndef _WIN32 struct radius_attribute attrib; #endif /* not _WIN32 */ if(debug) puts("policy: user_index"); /* * If deny_all_user == TRUE, it means that * userinfo database doesn't exist due to a corrupt acp_userinfo file. * This is a security breach and all users must be denied access * and event logged. */ if(deny_all_users) { syslog(LOG_ERR, "userinfo database is corrupted, denying access to\ all users. Check acp_userinfo file!\n"); (void)user_index_authorize(Acp, REQ_DENIED, upfield); if (ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_NOPROVIDE, Name); return; } /* * The switch, user_index_pend will cause one of two things to happen. * * Currently, this routine will not send a PEND back to the Annex, it * simply gets the user-specific information and sends the response * back to the Annex. If user_index_pend is #define'ed, then the Annex * will immediately send a PEND back to the Annex, get the user's * information and make the call to the Annex. * * If obtaining the user-specific information becomes too time-consuming, * the user_index_pend switch may be used to use the PEND mechanism instead. * */ #ifdef user_index_pend (void) bzero((char*)&up, sizeof(Uprof)); if (service == SERVICE_SECRET) (void)return_user_index(Acp, REQ_PENDING,up.up_secret); else if (service == SERVICE_MP) (void)return_max_links(Acp, REQ_PENDING, up.up_mp_max_links); else (void)return_user_index(Acp, REQ_PENDING, up.user_index); #endif env.annex = inet; /* ip addr. of annex requesting authentication*/ env.port = port; /* port no. where request is generated from */ env.ptype = ptype; env.protocol = service; /* cli, slip, ppp ?? */ env.regime = (struct security_regime *)NULL; /* what regime used */ env.group_list = (struct group_entry *)NULL; /* groups for user */ if (endpoint && endpoint->valid) { env.endpoint.class = endpoint->class; env.endpoint.length = endpoint->length; env.endpoint.valid = endpoint->valid; bcopy ((char*)endpoint->address,(char*)env.endpoint.address, endpoint->length); } else env.endpoint.valid = 0; /* Get the time-stamp for this action */ if (get_time_stamp(&(env.time)) == FALSE) syslog(LOG_ERR,"Failed to retrieve system time"); /* save the user's name */ strncpy(env.username, Name, LEN_USERNAME -1); env.username[LEN_USERNAME -1]= '\0'; #ifndef _WIN32 /* if we got max_links from radius, send it back */ Acp->env = &env; if (service == SERVICE_MP) { int rc = 0; int offset = 0; bzero((char*)&attrib, sizeof(struct radius_attribute)); attrib.type = PW_PORT_LIMIT; rc = ses_get_attribute(Acp->env, &attrib, &offset); if (rc > 0) /* found */ return_max_links(Acp, REQ_GRANTED, attrib.lvalue); else if (rc < 0) /* not found */ return_max_links(Acp, REQ_GRANTED, 1); if (rc != 0) /* radius user */ return; } #endif /* _WIN32 */ /* get user's entry from the userinfo database */ /* Initialize the various uprof structs */ #ifndef user_index_pend (void) bzero((char*)&up, sizeof(Uprof)); #endif error = get_user_profile_entry(&up, env.username, &env_p, &fileinfo); /* If user has a deny entry in userinfo entry, deny access. */ if(up.up_deny) { ret_err_code = REJECT_CODE(CODE_UNKNOWN,REJ_DENYUSER); (void)return_user_index(Acp, ret_err_code, upfield); if (ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, ret_err_code, Name); release_env(env_p); release_uprof(&up); return; } if (service == SERVICE_MP) { if (error == ACPU_ESUCCESS && up.up_mp_max_links) return_max_links(Acp, REQ_GRANTED, up.up_mp_max_links); else /* default to 1 */ return_max_links(Acp, REQ_GRANTED, 1); release_uprof(&up); return; } if (service == SERVICE_SECRET) bcopy(up.up_secret,upfield,sizeof(up.up_secret)); else bcopy(up.user_index,upfield,sizeof(up.user_index)); n = strlen(upfield); /* * if Dyndial_passwd/chap secret exists for the user * , annex is informed about it. */ #ifdef user_index_pend if ((error == ACPU_ESUCCESS) && (n > 0)) { (void)user_index_authorize(Acp, REQ_GRANTED, upfield); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_PROVIDE, Name); } else if ((error != ACPU_ESUCCESS) || (service != SERVICE_MP)) { (void)user_index_authorize(Acp, REQ_DENIED, upfield); if (ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_NOPROVIDE , Name); } #else if ((error == ACPU_ESUCCESS) && (n > 0)) { (void)return_user_index(Acp, REQ_GRANTED, upfield); if (ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_PROVIDE, Name); } else { (void)return_user_index(Acp, ret_err_code, upfield); if (ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, ret_err_code, Name); } #endif release_uprof(&up); return; } /* * ppp_security * * This interface is called whenever an Annex user logs in on a * PPP port, and the the NA parameters enable_security and * cli_security are Y and the security type for that port is PAP. * On virtual CLI's, cli_security (a per-port parameter) is * assumed to be Y. */ void ppp_security(Acp, logid, inet, port, ptype, service, direction, Name, Pass, opt_info) ACP *Acp; /* Handle to pass to library functions */ UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port, /* physical/virtual port number */ service; /* Expect SERVICE_{PPP,SLIP} */ int direction; /* direction of service */ ACP_USTRING Name; /* Username */ ACP_STRING Pass; /* password */ int ptype; ARQ_PROFILE *opt_info; { int valid = 0; int error; int regimes_passed = 0, regimes_tried = 0; /* vars for per-user security */ struct environment_spec env, *P_env = &env; Uprof up; /* storage for userinfo database entries */ char String[TOKEN_SIZE]; /* Any token of size 256 or less */ /* Zero out the Uprof structs */ (void) bzero((char*)&up, sizeof(Uprof)); Acp->auth.ret_err_code = REJECT_CODE(CODE_UNKNOWN, REJ_ERPCDDENY); /* supply return to procedure call so Annex does not timeout */ (void)return_serial_validate(Acp, REQ_PENDING); /* * userinfo database doesn't exist due to a corrupt acp_userinfo file, * this is a security breach and all users must be denied access and event * logged. */ if(deny_all_users) { syslog(LOG_ERR, "ppp_security: userinfo database is corrupted, denying access to all users. Check acp_userinfo file!\n"); (void)ppp_security_authorize(Acp,REQ_DENIED); if(ISUDP(Acp->state)) log_message(inet, logid, port, ptype, service, EVENT_REJECT, String); terminate_session(); return; } /* * If you return REQ_GRANTED here, then all connections are allowed * You can use this feature to bypass security for a group of ports */ if (Pass[0]=='\0' && Name[0]=='\0') { (void)ppp_security_authorize(Acp,REQ_DENIED); terminate_session(); return; } String[0] = 0; strcat(String,Name); /* * generic_authenticate_user authenticates the user and returns a value * based on the authentication results. */ Acp->env = &env; bzero((char*)&env, sizeof(struct environment_spec)); strncpy(env.username, Name, LEN_USERNAME - 1); Name[LEN_USERNAME - 1] = '\0'; env.annex = inet; env.port = port; env.ptype = ptype; env.protocol = service; Acp->auth.blacklisted = FALSE; Acp->chap = NULL; valid = generic_authenticate_user(Acp, logid, Name, Pass, FALSE, 1, String, opt_info); if(valid > 0) { /* * This call looks up userinfo database and chooses * the first entry that it finds and returns a pointer to the * entry. If the search fails, ie nothing for this user is found, * pointer points to an emtpy Uprof struct. */ error = get_user_profile_entry(&up, Name, &P_env, &fileinfo); if (debug && error) printf("user valid, but profile fetch failed: %d\n",error); } /* user has passed authentication & no deny in the userinfo entry. */ if ((valid > 0) && (error == ACPU_ESUCCESS || error == ACPU_ENOUSER) && (!up.up_deny)) { (void)ppp_security_authorize(Acp, REQ_GRANTED); if (ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_LOGIN, Name); } else /*user failed authentication or has been denied access in userinfo */ { /* if deny in userinfo, use the error code for log in acp_logfile */ if (up.up_deny) Acp->auth.ret_err_code = REJECT_CODE(CODE_UNKNOWN, REJ_DENYUSER); /* deny user with the appropriate error code */ (void)ppp_security_authorize(Acp, Acp->auth.ret_err_code); if (ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, Acp->auth.ret_err_code, String); /*user black listed but not denied in userinfo.*/ if (Acp->auth.blacklisted && (!up.up_deny)) log_message(inet, logid, port, ptype, service, EVENT_BLACKLIST, Name); } if (Acp->auth.hmask) acp_timer_enabled = 0; else if (ISTCP(Acp->state)) terminate_session(); release_uprof(&up); } /* * ipx_validate * * This interface is called whenever an Annex user logs in on a * IPX port. */ void ipx_validate(Acp, logid, inet, port, ptype, service, Name, Pass, Phone, Netnum, opt_info) ACP *Acp; /* Handle to pass to library functions */ UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port, /* physical/virtual port number */ service; /* Expect SERVICE_IPX */ ACP_USTRING Name; ACP_STRING Pass, Phone; int Netnum; int ptype; ARQ_PROFILE *opt_info; { char Message[TOKEN_SIZE]; /* error message to be logged. */ int blacklisted = FALSE; int valid = 0; /* user passed/faild authentication */ int error; /* success/failure for userinfo search */ int regimes_passed = 0, regimes_tried = 0; /* codes identifying regime and reason, used for logging */ /* vars for per-user security */ struct environment_spec env, *P_env = &env; struct env_gr_info envinfo; /* user's environment & group filenames */ Uprof up; /* storage for userinfo database entries */ Message[0] = '\0'; /* Zero out the Uprof structs */ (void) bzero((char*)&up, sizeof(Uprof)); Acp->auth.ret_err_code = REJECT_CODE(CODE_UNKNOWN, REJ_ERPCDDENY); /* * userinfo database doesn't exist due to a corrupt acp_userinfo file, * this is a security breach and all users must be denied access and event * logged. */ if(deny_all_users) { syslog(LOG_ERR, "ipx_validate: userinfo database is corrupted, denying access to all users. Check acp_userinfo file!\n"); (void)return_serial_validate(Acp, REQ_DENIED); if(ISUDP(Acp->state)) log_message(inet, logid, port, ptype, service, EVENT_REJECT, Message); if (Acp->auth.hmask) acp_timer_enabled = 0; else terminate_session(); return; } /* * generic_authenticate_user authenticates the user and returns a value * based on the authentication results. */ Acp->env = &env; bzero((char*)&env, sizeof(struct environment_spec)); strncpy(env.username, Name, LEN_USERNAME - 1); Name[LEN_USERNAME - 1] = '\0'; env.annex = inet; env.port = port; env.ptype = ptype; env.protocol = service; Acp->auth.blacklisted = FALSE; Acp->chap = NULL; valid = generic_authenticate_user(Acp, logid, Name, Pass, FALSE, 1, Message, opt_info); /*user's environment and group file info is saved. */ envinfo.env = &P_env; envinfo.gr_info = &fileinfo; /* * This call looks up userinfo database and chooses * the first entry that it finds and returns a pointer to the * entry. If the search fails, ie nothing for this user is found, * pointer points to an emtpy Uprof struct. */ error = get_user_profile_entry(&up, Name, &P_env, &fileinfo); /* Users has passed authentication and is not denied in userinfo */ if ((valid > 0) && (error == ACPU_ESUCCESS || error == ACPU_ENOUSER) && (!up.up_deny)) { #if (DEFAULT_NO_USERINFO > 0) Access ac_info; /* * Whether accesscode is defined in the userinfo * for this user. */ error = get_user_access(Name, "ipx", &ac_info, &envinfo); #endif /* * This if block checks whether this this is a * dialback request. "check_port_pool" checks * if this port is specified in the userinfo * database's port pool with the annex (inet). */ if ( (service == SERVICE_IPX_DIALBACK) || #if (DEFAULT_NO_USERINFO > 0) (error == ACPU_ENOUSER || error == ACPU_ENOACC) || #endif (check_port_pool(inet,port,ptype,(caddr_t)0) != DIAL_SUCC)) { (void)return_serial_validate(Acp, REQ_GRANTED); if (ISUDP(Acp->state)) log_message(inet, logid, port, ptype, service, EVENT_LOGIN, Name); } else { /* this is a dialback request */ ipx_check_dialback(Acp, logid, inet, port,ptype, service, Name, Netnum, Phone); } } else /* user is to be denied access */ { /* if there is a deny in user's userinfo entry */ if (up.up_deny) Acp->auth.ret_err_code = REJECT_CODE(CODE_UNKNOWN, REJ_DENYUSER); /* log appropriate message */ (void)return_serial_validate(Acp, Acp->auth.ret_err_code); if (Message[0]) { strcat(Message, ", "); if (Acp->auth.blacklisted) log_message(inet, logid, port,ptype, service, EVENT_BLACKLIST, Name); } strcat(Message, Name); if (ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, Acp->auth.ret_err_code, Message); } /* terminate (exit()) this session */ if (Acp->auth.hmask) acp_timer_enabled = 0; else terminate_session(); release_uprof(&up); return; } /* * port_to_annex() * * This interface is called whenever a user starts an Annex CLI, and * the NA parameters enable_security and cli_security are both Y. On * virtual CLI's, cli_security (a per-port parameter) is assumed to be Y. */ void port_to_annex(Acp, logid, inet, port,port_type, service, opt_info) ACP *Acp; /* Handle to pass to library functions */ UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port, /* physical/virtual port number */ service, /* Expect SERVICE_CLI{,_HOOK} */ port_type; /* port type virtual physical */ ARQ_PROFILE *opt_info; { ACP_USTRING Name; #ifndef _WIN32 ACP_LSTRING Pass; #else /* defined _WIN32 */ ACP_STRING Pass; #endif /* defined _WIN32 */ char Message[TOKEN_SIZE]; /* error message to be logged. */ int passed = NOT_VALIDATED; char nullstr[1]; Uprof up; int error; char *userprompt = ACP_USERPROMPT, *incorrect = ACP_INCORRECT, *permdenied = ACP_PERMDENIED; UINT32 mask = CLI_MASK, rcode = REQ_DENIED; char password_security = 0; int use_securid = 0, use_safeword = 0, s_passed = 0, p_passed = 0; int regimes_passed = 0, done = FALSE, regimes_tried = 0; struct environment_spec env, *P_env = &env; struct env_gr_info envinfo; /* user's environment & group filenames */ if(debug) puts("policy: port_to_annex"); nullstr[0] = '\0'; /* Initialize the various uprof structs */ (void) bzero((char*)&up, sizeof(Uprof)); Acp->auth.blacklisted = FALSE; Acp->auth.ret_err_code = REJECT_CODE(CODE_UNKNOWN, REJ_ERPCDDENY); /* supply return to procedure call so Annex does not timeout */ (void)return_port_to_annex(Acp,REQ_PENDING,mask,nullstr); /* * userinfo database doesn't exist due to a corrupt acp_userinfo file, * this is a security breach and all users must be denied access and event * logged. */ if(deny_all_users) { syslog(LOG_ERR, "port_to_annex: userinfo database is corrupted, denying access to all users. Check acp_userinfo file!\n"); (void)port_to_annex_authorize(Acp,rcode,mask, "userinfo-corrupt", 0); if(ISUDP(Acp->state)) log_message(inet, logid, port,port_type, service, EVENT_REJECT, Name); terminate_session(); return; } Message[0] = '\0'; /* * generic_authenticate_user authenticates the user and returns a value * based on the authentication results. If USER_VALIDATION !=1, then * user is not authenticated, however, this fxn. gives us the user's * environment which can be used to get cli commands. */ Acp->env = &env; bzero((char*)&env, sizeof(struct environment_spec)); env.annex = inet; env.port = port; env.ptype = port_type; env.protocol = service; Acp->auth.blacklisted = FALSE; Acp->chap = NULL; passed = generic_authenticate_user(Acp, logid, Name, Pass, TRUE, 3, Message, opt_info); /* user's environment and group file info is saved. */ envinfo.gr_info = &fileinfo; envinfo.env = &P_env; /* * This call looks up userinfo database and chooses * the first entry that it finds and returns a pointer to the * entry. If the search fails, ie nothing for this user is found, * pointer points to an emtpy Uprof struct. */ error = get_user_profile_entry(&up, Name, &P_env, &fileinfo); #if (USER_VALIDATION) /* * User has passed authentication. Now we'll * see if this is a dialback request, if not * we get user's environment from userinfo database */ if (passed > NOT_VALIDATED) /* authorize use of CLI */ { #if (DEFAULT_NO_USERINFO > 0) Access ac_info; int ret_user_access; #endif if ( (service == SERVICE_DIALBACK) || #if (DEFAULT_NO_USERINFO > 0) /* * lookup userinfo database for the user's entry * with accesscode information for dialback */ ((ret_user_access = get_user_access(Name, 0, &ac_info, &envinfo)) == ACPU_ENOUSER) || (ret_user_access == ACPU_ENOACC) || #endif /* * check the port pool in userinfo database only * when the port is "serial" since that's only * when dialback is allowed; ie. dialback is not * allowed for vcli connections. */ ((port_type != DEV_SERIAL && port_type != DEV_V120) || ((check_port_pool(inet, port,port_type, (caddr_t)0)) != DIAL_SUCC))) { /* If user has a deny entry in userinfo entry, deny access. */ if(up.up_deny) { Acp->auth.ret_err_code = REJECT_CODE(CODE_UNKNOWN,REJ_DENYUSER); (void)port_to_annex_authorize(Acp, Acp->auth.ret_err_code,mask, Name, 0); if (ISUDP(Acp->state)) log_message(inet, logid, port, port_type, service, Acp->auth.ret_err_code, Name); release_uprof(&up); return; } /* save climasks specified in userinfo entry, */ if (error == ACPU_ESUCCESS && up.up_climask != 0) mask = up.up_climask; /* * cli commands are specified in the user's userinfo * entry; save 'em and set rcode such that annex * expects cli commands with the authorization packet. */ #ifndef _WIN32 if (Acp->env->regime->regime_mask == RADIUS_MASK) { if (Acp->auth.cmd_list != NULL) { Acp->auth.hmask = (CHOOK_PROMPTING | CHOOK_BADCMND | CHOOK_GOODCMND); rcode = REQ_GRANT_HOOK; } else rcode = REQ_GRANTED; } else if (up.up_cmd_list && (service == SERVICE_CLI_HOOK || service == SERVICE_DIALBACK)) #else /* defined _WIN32 */ if (up.up_cmd_list && (service == SERVICE_CLI_HOOK || service == SERVICE_DIALBACK)) #endif /* defined _WIN32 */ { Acp->auth.hmask = (CHOOK_PROMPTING | CHOOK_BADCMND | CHOOK_GOODCMND); Acp->auth.cmd_list = up.up_cmd_list; rcode = REQ_GRANT_HOOK; } else rcode = REQ_GRANTED; /* log the successful log-in */ if (error == ACPU_ESUCCESS && ISUDP(Acp->state)) log_message(inet, logid, port, port_type, service, EVENT_LOGIN, Name); } else { /* this is a dialback request */ check_dialback(Acp, logid, inet, port,port_type, service, Name); } /* * Sending "permission granted" to the annex. * tcp code does away with "to" arguement but udp * still uses it. Garbage value leaves the vcli * connection hanging with R10.1 boot image */ if(rcode == REQ_GRANTED || rcode == REQ_GRANT_HOOK) outputstring(Acp, ACP_PERMGRANTD); if (debug) fprintf(stderr,"acp_policy.c: rcode = %d hmask = %d service = %d\n", rcode, Acp->auth.hmask, service); error = port_to_annex_authorize(Acp,rcode,mask,Name,Acp->auth.hmask); if (debug) fprintf(stderr,"acp_policy.c: called port_to_annex_authorize;\ returned value error = %d\n",error); } else { /* else reject and log */ (void) port_to_annex_authorize(Acp, Acp->auth.ret_err_code, mask, Message,0); if (ISUDP(Acp->state)) log_message(inet, logid, port,port_type, service, Acp->auth.ret_err_code, Message); if (Acp->auth.blacklisted) log_message(inet, logid, port,port_type, service, EVENT_BLACKLIST, Name); } /* user is denied access. */ #else /* !USER_VALIDATION */ /* with user validation disabled, return CLI mask, and log */ /* * cli commands are specified in the user's userinfo * entry; save 'em and set rcode such that annex * expects cli commands with the authorization packet. */ if (service == SERVICE_CLI_HOOK && up.up_cmd_list) { Acp->auth.hmask = (CHOOK_PROMPTING | CHOOK_BADCMND | CHOOK_GOODCMND); Acp->auth.cmd_list = up->up_cmd_list; rcode = REQ_GRANT_HOOK; } /* a generic authorize. */ else rcode = REQ_GRANTED; /* sending the authorize packet. */ (void)port_to_annex_authorize(Acp,rcode,mask,unknown,Acp->auth.hmask); /* log the event */ if (ISUDP(Acp->state)) log_message(inet, logid, port,port_type, service, EVENT_LOGIN, unknown); #endif /* USER_VALIDATION */ /* terminate (exit()) session */ if (Acp->auth.hmask) acp_timer_enabled = 0; else terminate_session(); release_uprof(&up); return; } /* * annex_to_net() * * This interface is called whenever a CLI user attempts to connect to * a machine with a call, rlogin, or telnet command; OR when a port * configured for dedicated mode attempts to connect to its dedicated * host. Enable_security and connect_security must be set to Y. * * The default policy is to scan the acp_restrict file for entries * which explicitly allow or disallow connection from the given * Annex to the given remote host. The file is searched sequentially. * If no entry is found which matches both the Annex and remote host, * either by name, Internet address, or wildcard, then permission is * granted. For example: * * *: snowwhite * annex01: harp, knight * annex02: harp, knight * 130.12.*~ 131.12.*, annex01, annex02 * 130.12.*: * * * : means restricted host, ~ means unrestricted host. No Annex may * connect to snowwhite; annex01 and annex02 may not connect to harp or * knight; Annexes on network 130.12 may only connect to other Annexes * on the same network, or to annex01 or annex02. */ void annex_to_net(Acp, logid, linet, port,ptype, service, rinet, Username, tcp_port_req) ACP *Acp; /* Handle to pass to library calls */ UINT32 logid, /* log file sequence number */ linet, /* Internet address of the Annex */ rinet; /* Internet address of target host */ int port, /* physical or virtual port number */ service, /* SERVICE_{CALL,RLOGIN, TELNET} */ tcp_port_req; /* tcp port number */ char *Username; /* Username associated with CLI */ int ptype; { char String[TOKEN_SIZE]; /* Any token of size 256 or less */ char Hostname[ACP_MAX_HOSTNAME_LEN]; /* ACP_MAX_HOSTNAME_LEN == 32 */ #ifndef _WIN32 struct environment_spec *env_p; /* storage for user's environment. */ struct security_regime *regime; /* storage for regime and passwd file */ #endif if(debug) puts("policy: annex_to_net"); /* * userinfo database doesn't exist due to a corrupt acp_userinfo file, * this is a security breach and all users must be denied access and * event logged */ if(deny_all_users) { syslog(LOG_ERR, "userinfo database is corrupted, denying access to\ all users. Check acp_userinfo file!\n"); goto error; } /* Inform Annex that policy decision is pending */ (void)return_annex_to_net(Acp, REQ_PENDING); /* Translate Remote Internet address for log message */ inet_name(Hostname, rinet); /* If USER_VALIDATION, include username in log message */ #if (USER_VALIDATION) /* include port in the message */ if (tcp_port_req < 0) (void)sprintf(String, "%s:%s", Hostname, Username); else (void)sprintf(String, "%s:%d:%s", Hostname, tcp_port_req, Username); #else /* no user validation, hence no username */ if (tcp_port_req < 0) (void)strcpy(String, Hostname); else (void)sprintf(String, "%s:%d", Hostname, tcp_port_req); #endif #ifndef _WIN32 /* * create user's environment. get time-stamp, and check * for restriction to be applied in acp_restrict * file (by calling available()) */ if((env_p = create_env())==NULL) { /* Log this event */ if (strlen(String)) { strcat(String, ","); } syslog(LOG_ERR,"Failed to allocate memory"); /* send reject authorization */ goto error; } /* Unable to allocate memory for environment. */ /* * No problem with memory allocation. We can proceed. * Set the annex address , port and service (cli/ppp/slip) */ env_p->annex = linet; env_p->port = port; env_p->ptype = ptype; env_p->protocol = service; /* Get the timestamp for this action */ if (get_time_stamp(&(env_p->time)) == FALSE) { /* Log this event */ if (strlen(String)) { strcat(String, ","); } syslog(LOG_ERR,"Failed to retrieve system time"); goto error; } strcpy(env_p->username,Username); if (create_group_list(&(env_p->group_list),env_p->username) == FALSE){ goto error; } /* * Check if the host requested for connection is * restricted for connection or not. Also * if the request is to start ppp or slip * connection (from a cli prompt), this * call will generate automatic filters for * the restrictions specified in the acp_restrict * file. */ if(available(env_p, rinet, tcp_port_req,DEV_ETHERNET, FALSE, NULL)) { /* if OK, authorize and log */ (void)annex_to_net_authorize(Acp, REQ_GRANTED); if(ISUDP(Acp->state)) log_message(linet, logid, port,ptype, service, EVENT_LOGIN, String); release_env(env_p); goto exit; } else goto error; /* reject and log */ /* free the allocated memory for environment */ #else /* _WIN32 */ /* if OK, authorize and log */ (void)annex_to_net_authorize(Acp, REQ_GRANTED); if(ISUDP(Acp->state)) log_message(linet, logid, port,ptype, service, EVENT_LOGIN, String); goto exit; #endif /* _WIN32 */ error: /* reject and log */ (void)annex_to_net_authorize(Acp, REQ_DENIED); if(ISUDP(Acp->state)) log_message(linet, logid, port,ptype, service, EVENT_REJECT, String); #ifndef _WIN32 release_env(env_p); #endif /* _WIN32 */ goto exit; exit: /* terminate (exit()) this session */ terminate_session(); /* dummy return which might never happen */ return; } /* * annex_to_lat() * * This interface is called whenever a CLI user attempts to * connect to a machine with a LAT connect command. * Enable_security and connect_security must be set to Y. * * The connection attempts are only logged. */ void annex_to_lat(Acp, logid, linet, port,ptype, service, Username, Service_name) ACP *Acp; /* Handle to pass to library calls */ UINT32 logid, /* log file sequence number */ linet; /* Internet address of the Annex */ int port, /* physical or virtual port number */ service; /* SERVICE_CONNECT */ char *Username, /* Username associated with CLI */ *Service_name; /* LAT service name */ int ptype; { char String[TOKEN_SIZE]; /* Any token of size 256 or less */ if(debug) puts("policy: annex_to_net"); /* Inform Annex that policy decision is pending */ (void)return_annex_to_net(Acp, REQ_PENDING); (void)annex_to_net_authorize(Acp, REQ_GRANTED); (void)sprintf(String, "%s:%s", Service_name, Username); if(ISUDP(Acp->state)) log_message(linet, logid, port,ptype, service, EVENT_LOGIN, String); /* terminate (exit()) this session */ terminate_session(); /* dummy return which might never happen */ return; } /* * net_to_port() * * Interface used by conversational port server when enable_security * and port_server_security are set to Y. In this case, the remote * host is the host from which the Annex was connected via telnet. * * Similar to the CLI security policy. If user's name/password is * not correct, the Annex will redisplay any rotary names, and will * continue with the port/rotary selection prompt. */ void net_to_port(Acp, logid, linet, port,ptype, service, rinet, opt_info) ACP *Acp; /* Handle to pass to library calls */ UINT32 logid, /* log file sequence number */ linet, /* Internet address of the Annex */ rinet; /* Internet address of remote host */ int port, /* physical or virtual port number */ service; /* service expected: SERVICE_PORTS */ int ptype; ARQ_PROFILE *opt_info; { char String[TOKEN_SIZE]; /* Any token of size 256 or less */ char Hostname[ACP_MAX_HOSTNAME_LEN];/* ACP_MAX_HOSTNAME_LEN == 32*/ ACP_USTRING Name; #ifndef _WIN32 ACP_LSTRING Pass; #else /* defined _WIN32 */ ACP_STRING Pass; #endif /* defined _WIN32 */ char *userprompt = ACP_USERPROMPT, *passprompt = ACP_PASSPROMPT, *permgrant = ACP_PERMGRANTD, *permdenied = ACP_PERMDENIED; char password_security = 0; int use_securid = 0, use_safeword= 0, s_valid = 0, p_valid = 0, error; /* declarations for per-user environment.*/ struct environment_spec env, *P_env = &env; /*storage for user's environment*/ int passed = NOT_VALIDATED; char Message[TOKEN_SIZE]; Uprof up; /* storage for userinfo entries */ /* Zero out the Uprof structs */ (void) bzero((char*)&up, sizeof(Uprof)); Acp->auth.blacklisted = FALSE; Acp->auth.ret_err_code= REJECT_CODE(CODE_UNKNOWN, REJ_ERPCDDENY); /* codes for regimes and reasons used for logging*/ #ifdef PASS_SEC password_security = 1; #endif if(debug) puts("policy: net_to_port"); /* Notify Annex of pending security decision */ (void)return_net_to_port(Acp, REQ_PENDING); /* Translate remote host's address for logging purposes */ inet_name(Hostname, rinet); #if (USER_VALIDATION) String[0] = '\0'; /* * Userinfo database doesn't exist due to a corrupt acp_userinfo file, * this is a security breach and all users must be denied access and * event logged */ if(deny_all_users) { syslog(LOG_ERR, "userinfo database is corrupted, denying access to\ all users. Check acp_userinfo file!\n"); (void)outputstring(Acp, permdenied); (void)net_to_port_authorize(Acp, REQ_DENIED, Name); if(ISUDP(Acp->state)) log_message(linet, logid, port,ptype, service, EVENT_REJECT, String); if (Acp->auth.hmask) acp_timer_enabled = 0; else terminate_session(); return; } Message[0]= '\0'; /* * generic_authenticate_user authenticates the user and returns a value * based on the authentication results. */ #ifdef PSERV_ONLY_USER if (acp_getusr(Name)) passed = VALIDATED; else passed = NOT_VALIDATED; #else Acp->env = &env; bzero((char*)&env, sizeof(struct environment_spec)); env.annex = linet; env.port = port; env.ptype = ptype; env.protocol = service; Acp->auth.blacklisted = FALSE; Acp->chap = NULL; passed = generic_authenticate_user(Acp, logid, Name, Pass, TRUE, 1, Message, opt_info); #endif if(passed) { /* * This call looks up userinfo database and chooses * the first entry that it finds and returns a pointer to the * entry. If the search fails, ie nothing for this user is found, * pointer points to an emtpy Uprof struct. */ error = get_user_profile_entry(&up, Name, &P_env, &fileinfo); } /* * If user has passed authentication and there is no deny specified * in the user's userinfo entry, send authorize packet to * annex. Else, deny access and log. */ if ((passed > NOT_VALIDATED) && (error == ACPU_ESUCCESS || error == ACPU_ENOUSER) && (!up.up_deny)) { (void)sprintf(String, "%s:%s", Hostname, Name); (void)outputstring(Acp, permgrant); (void)net_to_port_authorize(Acp, REQ_GRANTED, Name); if(ISUDP(Acp->state)) log_message(linet, logid, port,ptype, service, EVENT_LOGIN, String); } else { (void)sprintf(String, "%s:%s", Hostname, Name); if (up.up_deny) Acp->auth.ret_err_code = REJECT_CODE(CODE_UNKNOWN, REJ_DENYUSER); (void)outputstring(Acp, permdenied); (void)net_to_port_authorize(Acp, Acp->auth.ret_err_code, Name); if(ISUDP(Acp->state)) log_message(linet, logid, port,ptype, service, Acp->auth.ret_err_code, String); /* If user is black-listed, log in acp_logfile. */ if (Acp->auth.blacklisted &&(!up.up_deny)) log_message(linet, logid, port,ptype, service, EVENT_BLACKLIST, Name); } #else /* * If no user name/password validation is desired, * authorize unconditionally and log a message */ (void)sprintf(String, "%s:%s", Hostname, unknown); (void)net_to_port_authorize(Acp, REQ_GRANTED, unknown); if(ISUDP(Acp->state)) log_message(linet, logid, port,ptype, service, EVENT_LOGIN, String); #endif if (Acp->auth.hmask) acp_timer_enabled = 0; else terminate_session(); release_uprof(&up); return; } /* * port_to_annex_logout() * * This function is called remotely by the Annex when the CLI terminates. * Only if cli_security and enable_security were set to Y when CLI began. */ void port_to_annex_logout(Acp, logid, inet, port,ptype, service, Username) ACP *Acp; /* Handle for various ACP calls */ UINT32 logid, /* Log file sequence number */ inet; /* Annex Internet address */ int port, /* physical/virtual port number */ service; /* service, expect SERVICE_CLI */ char *Username; /* user associated with port */ int ptype; { /* generic acknowledge - return to remote caller */ (void)acp_acknowledge(Acp); /* log a message for accounting/security purposes */ #if (USER_VALIDATION) if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_LOGOUT, Username); #else if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_LOGOUT, unknown); #endif /* terminate this session, which exit()s */ /* terminate_session() is now in acp_logout_ppp_slip() in acp_rpc.c */ /* dummy return */ return; } /* * annex_to_net_logout() * * This function is called when a session of a CLI or dedicated port * ends for whatever reason. (reset, inactivity, hangup, kill, logout) */ void annex_to_net_logout(Acp, logid, linet, port, ptype,service, rinet, Username) ACP *Acp; /* Handle for ACP communication */ UINT32 logid, /* sequence number for logging */ linet, /* Annex Internet address */ rinet; /* Internet addr of remote host */ int port, /* Annex port number */ service; /* SERVICE_{RLOGIN,CALL,TELNET} */ char *Username; /* user associated with port */ int ptype; { char String[TOKEN_SIZE]; /* Any token of size 256 or less */ char Hostname[ACP_MAX_HOSTNAME_LEN]; /* ACP_MAX_HOSTNAME_LEN==32 */ /* generic acknowledge - return to remote caller */ (void)acp_acknowledge(Acp); /* log a message for accounting/security purposes */ inet_name(Hostname, rinet); /* If USER_VALIDATION, include username in log message */ #if (USER_VALIDATION) (void)sprintf(String, "%s:%s", Hostname, Username); #else (void)strcpy(String, Hostname); #endif if(ISUDP(Acp->state)) log_message(linet, logid, port,ptype, service, EVENT_LOGOUT, String); /* terminate this session, which exit()s */ terminate_session(); /* dummy return */ return; } /* * annex_to_lat_logout() * * This function is called when a session of a LAT connect * ends for whatever reason. */ void annex_to_lat_logout(Acp, logid, linet, port,ptype, service, Username, Service_name) ACP *Acp; /* Handle for ACP communication */ UINT32 logid, /* sequence number for logging */ linet; /* Annex Internet address */ int port,ptype, /* Annex port number */ service; /* SERVICE_CONNECT */ char *Username, /* user associated with port */ *Service_name; /* LAT service name */ { char String[TOKEN_SIZE]; /* Any token of size 256 or less */ /* generic acknowledge - return to remote caller */ (void)acp_acknowledge(Acp); /* log a message for accounting/security purposes */ (void)sprintf(String, "%s:%s", Service_name, Username); if(ISUDP(Acp->state)) log_message(linet, logid, port,ptype, service, EVENT_LOGOUT, String); /* terminate this session, which exit()s */ terminate_session(); /* dummy return */ return; } /* * net_to_port_logout() * * Called when Annex closes a previously secured port server connection. */ void net_to_port_logout(Acp, logid, linet, port,ptype, service, rinet, Username) ACP *Acp; /* Handle for ACP library */ UINT32 logid, /* log sequence number */ linet, /* Annex Internet address */ rinet; /* remote host's Internet */ int port,ptype, /* physical/virtual port */ service; /* service SERVICE_PORTS */ char *Username; /* user associated w/port */ { char String[TOKEN_SIZE]; /* Any token of size 256 or less */ char Hostname[ACP_MAX_HOSTNAME_LEN]; /* ACP_MAX_HOSTNAME_LEN==32 */ /* generic acknowledge - return to remote caller */ (void)acp_acknowledge(Acp); /* log a message for accounting/security purposes */ inet_name(Hostname, rinet); #if (USER_VALIDATION) (void)sprintf(String, "%s:%s", Hostname, Username); #else (void)sprintf(String, "%s:%s", Hostname, unknown); #endif if(ISUDP(Acp->state)) log_message(linet, logid, port,ptype, service, EVENT_LOGOUT, String); /* terminate this session, which exit()s */ /* terminate_session() is now in acp_logout_ppp_slip() in acp_rpc.c */ /* dummy return */ return; } /* * appletalk_profile * * This function is called by the annex when a user starts an * ARAP or ATCP/PPP session on the annex. */ void appletalk_profile(Acp, logid, inet, port,ptype, service, Name, nve) ACP *Acp; /* Handle to pass to library functions */ UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port,ptype, /* physical/virtual port number */ nve, /* tell me nve details */ service; /* Expect SERVICE_DIALUP */ ACP_USTRING Name; /* Username */ { Uprof up; int error; /* error values returned from userinfo search */ int guest; struct environment_spec *env_p; /* user's environment */ UINT32 ret_err_code = REJECT_CODE(CODE_UNKNOWN, REJ_ERPCDDENY); /* error codes for regime and reason, used for logging*/ if(debug) puts("policy: appletalk_profile"); /* Initialize the various uprof structs */ (void) bzero((char*)&up, sizeof(Uprof)); /* * Userinfo database doesn't exist due to a corrupt acp_userinfo file, * this is a security breach and all users must be denied access and * event logged */ if(deny_all_users) { syslog(LOG_ERR, "userinfo database is corrupted, denying access to\ all users. Check acp_userinfo file!\n"); return_appletalk_profile(Acp,REQ_DENIED, 0, 0, 0, 0, 0, 0,0,0,0 ); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_NOPROVIDE, Name); return; } /* * create user's environment by allocating storage, * get time-stamp, lookup userinfo database for user's * entry (if any) and check for deny keyword in the user's * entry. */ if((env_p = create_env()) == NULL) { /* do not proceed with the connection and log the instance. */ return_appletalk_profile(Acp,REQ_DENIED, 0, 0, 0, 0, 0, 0,0,0,0 ); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_NOPROVIDE, Name); syslog(LOG_ERR, "appletalk_profile: failed to allocate environment for the user; acces denied. \n"); return; } /* failed to allocate storage for the user's environment. */ /* * set the annex's (which made the authentication request) * internet address, port no., service (cli/ppp/slip), * intialize storage for regime name & password file * and group list (link list of groups in which this user * belongs). */ env_p->annex = inet; env_p->port = port; env_p->ptype = ptype; env_p->protocol = service; env_p->regime = (struct security_regime *)NULL; env_p->group_list = (struct group_entry *)NULL; /* Get the timestamp for this action */ if (get_time_stamp(&(env_p->time)) == FALSE) syslog(LOG_ERR,"Failed to retrieve system time"); /* Log this event */ strncpy(env_p->username, Name, LEN_USERNAME -1); env_p->username[LEN_USERNAME -1]= '\0'; /* get user's entry from the userinfo database */ error = get_user_profile_entry(&up,env_p->username,&env_p,&fileinfo); /* * If user has a deny entry in userinfo entry, deny access * and log the event */ if(up.up_deny) { ret_err_code = REJECT_CODE(CODE_UNKNOWN,REJ_DENYUSER); return_appletalk_profile(Acp,ret_err_code, 0, 0, 0, 0, 0, 0,0,0,0 ); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, ret_err_code, Name); release_env(env_p); release_uprof(&up); return; } /* set guest to true if a guest connection request. */ guest = !(strcmp("",Name)); /* * if an entry is found in the userinfo database check * to see if any apple talk info. is specified, for * instance at_passwd, zonelist, nve filters etc. * If yes, proceed else do not proceed. * * If no entry is found and user is logging in as a * guest, proceed , else do not proceed. */ if (error == ACPU_ESUCCESS) { if ( (up.up_at.at_zone_combined != 0) || (up.up_at.at_zones != 0) || (strlen(up.up_at.at_zonelist) != 0) || (strlen(up.up_at.at_passwd) != 0) || (up.up_at.at_connect_time != 0) || (up.up_at.at_nve_exclude != 0) || (strlen(up.up_at.at_nve) != 0) ) { return_appletalk_profile(Acp,REQ_GRANTED, up.up_at.at_zone_combined, up.up_at.at_zones, up.up_at.at_zonelist, up.up_at.at_passwd, up.up_at.at_connect_time, up.up_at.at_nve_exclude, (nve ? up.up_at.at_nve : 0), (nve ? up.up_at.at_nve_combined : 0), (nve ? (up.up_at.at_nves / 3) : 0)); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_LOGIN, Name); } else { return_appletalk_profile(Acp,REQ_DENIED, 0,0,0,0,0,0,0, 0,0); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype,service,EVENT_NOPROVIDE,Name); } } else if (error == ACPU_ENOUSER && guest) { return_appletalk_profile(Acp,REQ_GRANTED, 0, 0, 0, 0, 0,0,0,0,0); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_LOGIN, Name); } else { return_appletalk_profile(Acp,REQ_DENIED, 0, 0, 0, 0, 0, 0,0,0,0 ); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_NOPROVIDE, Name); } /* free the allocated memory for the user's environment. */ release_env(env_p); release_uprof(&up); return; } /* * max_logon_val * * This function is called by the annex when a port is started */ void max_logon_val(Acp, logid, inet, port, service, Name, to) ACP *Acp; /* Handle to pass to library functions */ UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port, /* physical/virtual port number */ service; /* Expect SERVICE_DIALUP */ ACP_USTRING Name; /* Username */ struct sockaddr_in *to; /* who sent the message we will respond to */ { char Message[TOKEN_SIZE]; Uprof up; int error; struct environment_spec *env_p; UINT32 ret_err_code = REJECT_CODE(CODE_UNKNOWN, REJ_ERPCDDENY); #ifndef _WIN32 struct radius_attribute attrib; int rc, offset; #endif /* not _WIN32 */ if(debug) puts("policy: max_logon_val"); Message[0] = '\0'; /* Initialize the various uprof structs */ (void) bzero(&up, sizeof(Uprof)); /* * userinfo database doesn't exist due to a corrupt acp_userinfo file, * this is a security breach and all users must be denied access */ if (debug) printf("the value of \"deny_all_user\" is %d \n", deny_all_users); if(deny_all_users) { syslog(LOG_ERR, "userinfo database is corrupted, denying access to\ all users. Check acp_userinfo file!\n"); return_max_logon_tcp(Acp,REQ_DENIED, 0); if(ISUDP(Acp->state)) { if (debug) puts("error1"); log_message(inet, logid, port, service, EVENT_NOPROVIDE, Name); } return; } env_p = create_env(); if (env_p) { env_p->annex = inet; env_p->port = port; env_p->protocol = service; env_p->regime = (struct security_regime *)NULL; env_p->group_list = (struct group_entry *)NULL; /* Get the timestamp for this action */ if (get_time_stamp(&(env_p->time)) == FALSE) { /* Log this event */ syslog(LOG_ERR, "Failed to retrieve system time"); } strncpy(env_p->username, Name, LEN_USERNAME -1); env_p->username[LEN_USERNAME -1]= '\0'; /* MAKING TWO CALLS INSTEAD OF ONE. */ /* FIRST CALL SEARCHES THE DATABASE */ /* FOR PRE PER-USER ENTRIES AND THE */ /* 2ND CALL LOOKS FOR ENTRIES WITH */ /* THE PER-USER ENV-STRING. WHATEVER*/ /* IS THE FIRST MATCH IS USED. M_ALI*/ /*check for ACPU_ESUCCESS =0& ACPU_ENOUSER =4*/ /* new style*/ #ifndef _WIN32 Acp->env = env_p; rc = offset = 0; bzero((char*)&attrib, sizeof(struct radius_attribute)); attrib.type = PW_SESSION_TIMEOUT; rc = ses_get_attribute(Acp->env, &attrib, &offset); if (rc > 0 && attrib.lvalue > 0) /* found */ return_max_logon_tcp(Acp, REQ_GRANTED, 1 + ((attrib.lvalue - 1) / 60)); else if (rc != 0) /* not found */ return_max_logon_tcp(Acp, REQ_DENIED, 0); if (rc != 0) /* radius user */ return; #endif /* _WIN32 */ error = get_user_profile_entry(&up, env_p->username, &env_p, &fileinfo); /* If user has a deny entry in userinfo entry, deny access. */ if(up.up_deny) { ret_err_code = REJECT_CODE(CODE_UNKNOWN,REJ_DENYUSER); return_max_logon_tcp(Acp,ret_err_code, 0); if(ISUDP(Acp->state)) { if (debug) puts("error2"); log_message(inet, logid, port, service, ret_err_code, Name); } release_uprof(&up); return; } /* value of 0 is invalid. Value of -1 means immediate logout of user */ /* The code that looks up the acp_userinfo database returns a 0 if the max_logon parameter is not specified. */ if ((error == ACPU_ESUCCESS) && (up.up_max_logon != 0)) { return_max_logon_tcp(Acp, REQ_GRANTED, up.up_max_logon); if (ISUDP(Acp->state)) { if (debug) printf("udp error\n"); log_message(inet, logid, port, service, EVENT_LOGIN, Name); } } else { return_max_logon_tcp(Acp, REQ_DENIED, 0); if (ISUDP(Acp->state)) { if (debug) printf("udp error\n"); log_message(inet, logid, port, service, EVENT_NOPROVIDE, Name); } } /* free the allocated memory for the user's environment. */ release_env(env_p); } /* if env_p exists */ else { /* impose no time restriction */ return_max_logon_tcp(Acp, REQ_DENIED, 0); if(ISUDP(Acp->state)) { if (debug) puts("error4"); log_message(inet, logid, port, service, EVENT_NOPROVIDE, Name); } syslog(LOG_ERR, "max_logon_val: failed to allocate environment for\ the user; acces denied. \n"); }/* failed to allocate storage for the user's environment. */ release_uprof(&up); return; } int hook_callback(Acp,logid,code,cmnd) ACP *Acp; /* Handle to pass to library functions */ int code; char *cmnd; { int rcode = HCB_RETURN; /* * This call permits ACP to print back to the user. The "pending" code * tells the Annex to wait for hook_callback_string instead, and to * process any outputstring or promptstring requests in the meantime. */ return_hook_callback(Acp,HCB_PENDING,NULL); /* * Return the possibly modified command to the CLI. This call frees up * the CLI and restarts execution on the Annex. */ if (code == HCB_BAD_COMMAND) { /* a parsing error occurred */ sprintf(syslogbuf, "bad clicmd \"%s\" in acp_userinfo\n", cmnd); syslog(LOG_ERR, syslogbuf); if (Acp->auth.cmd_list != NULL) /* was it a clicmd? */ rcode = HCB_LAST_CMND; /* yes, kill the CLI */ else /* no, this should never happen */ rcode = HCB_TERMINATE; /* so terminate the RACP session */ } cmnd = ""; if (code == HCB_BEFORE_PROMPT) /* the CLI wants to print a prompt */ if (Acp->auth.cmd_list == NULL) /* have we any clicmds to send? */ rcode = HCB_TERMINATE; /* no, terminate the RACP session */ else { /* yes */ if (debug) printf("acp_policy.c: clicmd \"%s\"\n", Acp->auth.cmd_list->clicmd); if (*Acp->auth.cmd_list->clicmd == '\0') { rcode = HCB_TERMINATE; Acp->auth.cmd_list = NULL; } else { cmnd = Acp->auth.cmd_list->clicmd; if ((Acp->auth.cmd_list = Acp->auth.cmd_list->next) == NULL) rcode = HCB_LAST_CMND; else rcode = HCB_RETURN; } } return(hook_callback_string(Acp,rcode,cmnd)); } /* * dialup_address_validate() * * Look up username in (INSTALL_DIR/acp_dialup) file. * Return local and remote addresses associated with first * instance of this username and Annex. * */ static UINT32 dialup_address_validate(User, inet_addr, type, loc, rem, node, port, port_type, filters, routes, service, dialup_flags) char *User; UINT32 inet_addr, *loc, *rem; int type; int port, port_type; u_char *node; STR_LIST **filters, **routes; int service; UINT32 *dialup_flags; { char *name ; int error; UINT32 inet, local=0, remote=0; int found_match = 0; int errorcode,i = 0; struct environment_spec env, *env_p = &env; Uprof up; STR_LIST *strlist, *slp; struct cli_cmd_list *clist; int radius_user = 0; #ifdef _WIN32 char user_and_domain[64]; char *ptr_user_name; #else struct radius_attribute attrib; int offset = 0; #endif if (filters) *filters = NULL; if (routes) *routes = NULL; *dialup_flags = REQ_GRANTED; /* assuming everything will be OK */ bzero((char*)&up, sizeof(Uprof)); env.annex = inet_addr; env.port = port; env.ptype = port_type; env.protocol = service; env.regime = (struct security_regime *)NULL; env.group_list = (struct group_entry *)NULL; /* Get the timestamp for this action */ get_time_stamp(&(env.time)); strcpy(env.username, User); #ifndef _WIN32 radius_user = ses_lookup(&env, NULL, SDB_FINDACTIVEUSER); #endif /* * This call looks up userinfo database and chooses * the first entry that it finds and returns a pointer to the * entry. If the search fails, ie nothing for this user is found, * pointer points to an emtpy Uprof struct. */ error = get_user_profile_entry(&up, env.username, &env_p, &fileinfo); if (filters && (error == ACPU_ESUCCESS) && up.up_filter_list) { if (debug) printf("dialup_address_validate: fetched filters.\n"); strlist = racp_create_strlist(up.up_filter_list->clicmd, strlen(up.up_filter_list->clicmd)); for(slp = strlist, clist=up.up_filter_list->next; clist && slp; clist=clist->next, slp=slp->next) slp->next = racp_create_strlist(clist->clicmd, strlen(clist->clicmd)); *filters = strlist; } if (filters) { create_group_list(&(env_p->group_list),env_p->username); if (available(env_p, 0, 0,0, TRUE, filters) == ACCESS_RESTRICTED) { /* error messages and syslogs come from available() */ if (*filters) { if (debug) printf("dialup_address_validate: discarding filters.\n"); racp_destroy_strlist_chain(*filters); *filters = NULL; } *dialup_flags = REQ_DENIED; /* failed to get filters. */ release_uprof(&up); return(0); } } if (routes) { #ifndef _WIN32 if (radius_user) { bzero((char*)&attrib, sizeof(struct radius_attribute)); attrib.type = PW_FRAMED_ROUTE; strlist = slp = NULL; offset = 0; if (ses_get_attribute(&env, &attrib, &offset) > 0) { if (debug_dialup) printf("dialup_address_validate: fetched routes; \"%s\"\n", attrib.strvalp); slp = strlist = xlate_route_attrib(&attrib); free(attrib.strvalp); bzero((char*)&attrib, sizeof(struct radius_attribute)); attrib.type = PW_FRAMED_ROUTE; while(ses_get_attribute(&env, &attrib, &offset) > 0) { if (debug_dialup) printf("\t\"%s\"\n", attrib.strvalp); if (slp != NULL) slp->next = xlate_route_attrib(&attrib); else slp = strlist = xlate_route_attrib(&attrib); if (slp != NULL && slp->next != NULL) slp = slp->next; free(attrib.strvalp); bzero((char*)&attrib, sizeof(struct radius_attribute)); attrib.type = PW_FRAMED_ROUTE; } } /* Construct from Framed-Netmask */ offset = 0; bzero((char*)&attrib, sizeof(struct radius_attribute)); attrib.type = PW_FRAMED_NETMASK; if (ses_get_attribute(&env, &attrib, &offset) > 0) { UINT32 netmask = attrib.lvalue; offset = 0; bzero((char*)&attrib, sizeof(struct radius_attribute)); attrib.type = PW_FRAMED_ADDRESS; if (ses_get_attribute(&env, &attrib, &offset) > 0) { if (strlist == NULL) strlist = netmask_to_route(netmask, attrib.lvalue); else { if (slp == NULL) { /* find the end again */ for(slp = strlist; slp->next != NULL; slp = slp->next); } slp->next = netmask_to_route(netmask, attrib.lvalue); } } } *routes = strlist; } else if (up.up_route_list) #else /* defined _WIN32 */ if (up.up_route_list) #endif /* defined _WIN32 */ { if (debug_dialup) printf("dialup_address_validate: fetched routes; \"%s\"\n", up.up_route_list->clicmd); strlist = racp_create_strlist(up.up_route_list->clicmd, strlen(up.up_route_list->clicmd)); for(slp = strlist, clist=up.up_route_list->next; clist; clist=clist->next, slp=slp->next) { if (debug_dialup) printf("\t\"%s\"\n", clist->clicmd); slp->next = racp_create_strlist(clist->clicmd, strlen(clist->clicmd)); } *routes = strlist; } } #ifndef _WIN32 if (radius_user) { bzero((char*)&attrib, sizeof(struct radius_attribute)); if (type == IPX_ADDRT) attrib.type = PW_FRAMED_IPXNET; else attrib.type = PW_FRAMED_ADDRESS; offset = 0; if ((found_match = ses_get_attribute(&env, &attrib, &offset)) > 0) { switch(attrib.lvalue) { case 0xfffffffe: /* RADIUS let NAS choose */ found_match = FALSE; break; case 0xffffffff: /* RADIUS let user choose for IP, unsupported */ if (type == IP_ADDRT) /* fall back to let NAS choose */ found_match = FALSE; break; default: break; } *rem = remote = attrib.lvalue; *loc = inet_addr; } if (found_match < 0) found_match = FALSE; } #else /* defined _WIN32 */ if ( 0 ) /* dummy if to match following else */ { ; } #endif /* defined _WIN32 */ else { if (setacpdialup() < 0) goto goback; if (debug_dialup) { LINE; printf("Data from Annex:\nname: %-10s inet: %8x port: %d\n", User, inet_addr,port); LINE; printf("Data from acp_dialup file:\n"); fflush(stdout); } errorcode = findacpdialup(&name, &inet, type, &local, &remote, node, port, port_type, User, inet_addr, dialup_flags); if (errorcode == -1) { if (debug_dialup) { printf("----- Reached end of acp_dialup file. -----\n"); LINE; } goto goback; } /* * If we got a valid entry from getacpdialup() and * the username and inet addresses match, then * give the corresponding local and remote addresses * and return. */ if (errorcode == 1) { if (debug_dialup) { struct in_addr in; in.s_addr = inet; printf("\nname: %-10s inet: %s ",name,inet_ntoa(in)); if (type == IPX_ADDRT) printf("net: %8x node: %02x-%02x-%02x-%02x-%02x-%02x", remote,node[0],node[1],node[2],node[3],node[4], node[5]); else { in.s_addr = local; printf("loc: %s ",inet_ntoa(in)); in.s_addr = remote; printf("rem: %s",inet_ntoa(in)); } LINE; fflush(stdout); } #ifdef _WIN32 ptr_user_name = PrependDomainNameAndFix(User, user_and_domain); if (found_match = (!_strnicmp(ptr_user_name, name, ACP_MAXUSTRING) && ( inet == 0 || inet == inet_addr))) { *loc = local == 0 ? inet_addr : local; *rem = remote; } #else if (found_match = (!strncmp(User, name, ACP_MAXUSTRING) && ( inet == 0 || inet == inet_addr))) { *loc = local == 0 ? inet_addr : local; *rem = remote; } #endif } goback: endacpdialup(); } release_uprof(&up); if (filters || routes || found_match) { return 1; } else { *dialup_flags = REQ_DENIED; return 0; } } /* * NTValidate(char *User, char *Password) * This routine is used for authenticating users when he security server * is running on an NT host. * IN User Username * IN Password Password entered by the user * Returns: * found_match - True/False. found user&password in the database. */ #ifdef _WIN32 int NTValidate(char *User, char *Password) { int found_match = 0; HANDLE hUserToken; char *Domain, *UserName; char buf[MAX_OPTION]; int i; /*strcpy(buf, User); buf[MAX_OPTION-1]=0; */ strncpy(buf, User, MAX_OPTION-1);/*spr10133 & spr10146: rbhatt */ buf[MAX_OPTION-1] = '\0'; Domain = szDefaultDomain; UserName = buf; if (debug) printf("User=<%s>, Pass=<%s>\n", User, Password); /* parse for domain and user name */ for (i=0; buf[i]; i++) if (buf[i] == '\\') { buf[i]=0; Domain = &buf[0]; UserName = &buf[i+1]; break; } /* search the NT database for the username and password specified */ found_match = LogonUser(UserName, Domain, Password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &hUserToken); /* results of the search */ if (debug) printf("__found_match=%d_______User=<%s>, Pass=<%s>_____%s;%s\n", found_match, User, Password, Domain, UserName); if (found_match && ErpcdOpt->UseGroupAuthentication) { if (debug) printf("-------------------------UseGroupAuthentication\n"); found_match = xyRasValidate(Domain, UserName); } if(found_match) CloseHandle(hUserToken); return found_match; } #endif /* _WIN32 */ /* * acp_validate() * * Look up name and password combination in an /etc/passwd compatible * password file (INSTALL_DIR/acp_passwd. If several passwords exist * for the same username, any valid password is accepted. Null entries * and "*" entries (disabled) for passwords are not considered valid. * * Additionally, for systems using a shadow file (/etc/shadow or * INSTALL_DIR/acp_shadow), this file is checked for a valid password * and the appropriate aging is performed if at least one password entry * in the /etc/passwd file is "x". * * IN User username * IN Password User's password. * IN Passwd password file path. * OUT blacklist blacklist this user; depends on how blacklisting * parameters are set. * * Return codes: * 0 - invalid username/password (or account expired) * 1 - valid username/password * 2 - valid username/password, but password to expire soon * 3 - valid username/password, but password has expired * 4 - valid username/password, but account to expire soon * * In cases 2 and 4, the global variable "daysleft" is set to the number of * days left until this event. */ int acp_validate(User, Password, blacklist, Passwd) char *User, *Password; int *blacklist; char *Passwd; { #ifdef _WIN32 return NTValidate(User, Password); #else char str[ACP_LONGSTRING]; /* ACP_LONGSTRING == 80 */ int rv=0, user_found = FALSE; struct passwd *pwd; char *pw; int found_match = 0; #ifdef USESHADOW struct spwd *spwd; int found_x = 0; INT32 today,expire; #endif if(setacppw(Passwd) == -1) return 0; /* the password file is invalid */ /* * Look in the password file until an entry with the "User" name * is found. Once the right entry is found, encrypt the user's * password and match with the entry found. */ while ((pwd = getacppw()) != NULL) { if(debug) fprintf(stderr, "checking user %s...\n", pwd->pw_name); if (pwd->pw_passwd[0] == '\0') continue; /* Disallow null passwords */ /*find the password file entry with the user*/ if (strncmp(pwd->pw_name, User, ACP_MAXUSTRING) != 0) continue; else user_found = TRUE; #ifdef USESHADOW if(debug) fprintf(stderr, "found user, now check for x\n"); if (strcmp("x",pwd->pw_passwd) == 0) { found_x = 1; if (debug) fprintf(stderr, "found_x is true\n"); break; } #endif /* encrypt user's password and match with the file's entry */ pw = crypt(Password, pwd->pw_passwd); if (strcmp(pw, pwd->pw_passwd) == 0) { found_match = 1; break; } } /* close the file pointer. */ endacppw(); #ifdef USESHADOW /* Currently will use the default acp_shadow */ if (found_match) return 1; if (!found_x) return 0; today = DAY_NOW; /* open the shadow file for reading */ setacpsp(); /* read the shadow file until the user's entry is found. */ while ((spwd = getacpsp()) != NULL) { /* Disallow null passwords */ if (spwd->sp_pwdp[0] == '\0') continue; if (strncmp(spwd->sp_namp, User, ACP_MAXUSTRING) != 0) continue; else user_found = TRUE; pw = crypt(Password, spwd->sp_pwdp); if (strncmp(pw, spwd->sp_pwdp, ACP_MAXSTRING) != 0) continue; /* Check for an expired account */ #ifndef SCO if (spwd->sp_expire > 0 && today > spwd->sp_expire) break; #endif /* * We're not implementing sp_inact here to check for inactive accounts. * To do this, the time/date stamp in /usr/adm/wtmp should be checked * and validated. This would probably also require writing to this * file to update this stamp after the user logs into the Annex. */ /* Calculate password expiration date */ if (spwd->sp_lstchg > 0 && spwd->sp_max > 0) expire = spwd->sp_lstchg+spwd->sp_max; else expire = 0; /* Check for an expired password */ if (today < spwd->sp_lstchg || (expire > 0 && today > expire) || (spwd->sp_lstchg == 0)) { strncpy(old_password,spwd->sp_pwdp, sizeof(old_password)); found_match = 3; /* Check for an account that is soon to expire */ #ifndef SCO } else if (spwd->sp_expire > 0 && today >= spwd->sp_expire-spwd->sp_warn) { found_match = 4; daysleft = spwd->sp_expire-today; /* Check for a password that is soon to expire */ } else if (expire > 0 && today >= expire-spwd->sp_warn) { found_match = 2; daysleft = expire-today; #endif } else found_match = 1; break; } /* close the shadow file */ endacpsp(); #endif #ifdef USE_NDBM /* Search acp_dbm for this user to verify that he is not blacklisted. */ if(user_found == TRUE) { DBM *dbm; rv = erpcd_lock_acp_dbm(); if(rv == 0) { sprintf(str, "%s/", install_dir); strcat(str, ACP_DBM_FILE); dbm = dbm_open(str, (O_CREAT | O_RDWR), 0600); /* successfully openend database. */ if(dbm != NULL) { if(found_match != 0) { /* * check whether user is blacklisted and then record * the login success */ rv = dbm_verify_login_success(dbm, User); if((rv != TRUE && errno != ENOENT) || rv == FALSE) { if (debug) printf("user blacklisted!\n"); found_match = 0; } if(rv < 0 && errno != ENOENT) process_error(User, rv); } else { if( maxcon != -1 || maxtotal != -1) { /* record login failure and determine whether to blacklist user */ rv = dbm_record_login_failure(dbm, User, maxcon, maxtotal, period); } if(rv < 0 ) { process_error(User, rv); } else if ((rv == RVBLACKLIST_MAXTRIES) || (rv == RVBLACKLIST_OVERTME)) { (void)erpcd_suspectAttackTrap (User, rv); if (blacklist != NULL) *blacklist = TRUE; } } unlock_database(dbm); } else { #ifdef USE_SYSLOG syslog(LOG_CRIT, "%m"); #endif if (debug) printf("Can't open dbm file\n"); found_match = 0; dbm_unlock_acp_dbm(); } } else { if (debug) printf("Can't lock dbm file\n"); found_match = 0; dbm_unlock_acp_dbm(); } user_found = FALSE; } #endif /* USE_NDBM */ if (debug > 1) printf("acp_validate returning %d\n",found_match); return found_match; #endif /* _WIN32 */ } /* * acp_kerberos_validate() * * Look up name and password combination in an /etc/passwd compatible * password file (INSTALL_DIR/acp_passwd). If several passwords exist * for the same username, any valid password is accepted. Null entries * and "*" entries (disabled) for passwords are not considered valid. * * Additionally, for systems using a shadow file (/etc/shadow or * INSTALL_DIR/acp_shadow), this file is checked for a valid password * and the appropriate aging is performed if at least one password entry * in the /etc/passwd file is "x". * * IN User username * IN Password password provided by "User". * IN Passwd passwd/ticket directory. * Return codes: * -1 - regime is unavailable * 0 - invalid username/password (or account expired) * 1 - valid username/password * 2 - valid username/password, but password to expire soon * 3 - valid username/password, but password has expired * 4 - valid username/password, but account to expire soon * * In cases 2 and 4, the global variable "daysleft" is set to the number of * days left until this event. */ int acp_kerberos_validate(User, Password, Passwd) char *User, *Password, *Passwd; { #ifdef KERBEROS char tktfile[MAXPATHLEN]; char krbrlm[REALM_SZ], uname[ANAME_SZ]; int krbval, rv; /* resuts for calls to kerberos std api */ /* Whether kerberos is available */ if ((rv=krb_get_lrealm(krbrlm, 1)) == UNAVAILABLE) return (UNAVAILABLE); else if (rv != KSUCCESS) { (void)strncpy(krbrlm, KRB_REALM, sizeof(krbrlm)); } /* * Use our pid for the ticket file name; it goes * away immediately, anyway */ (void)sprintf(tktfile,"%s%d", KRB_TK_DIR, getpid()); krb_set_tkt_string(tktfile); strcpy(uname, User); krbval = krb_get_pw_in_tkt(uname, "", krbrlm, "krbtgt", krbrlm, DEFAULT_TKT_LIFE, Password); bzero((char*)Password, strlen(Password)); dest_tkt(); switch(krbval) { case INTK_OK: if (debug) printf("Krb-auth worked for user %s\n", User); return(1); default: if (debug) printf("Krb-auth FAILED for user %s (%s)\n", User, krb_err_txt[krbval]); } #endif /* KERBEROS */ return(0); } /* * acp_native_validate() * * Look up name and password combination in an /etc/passwd compatible * password file (INSTALL_DIR/acp_passwd). If several passwords exist * for the same username, any valid password is accepted. Null entries * and "*" entries (disabled) for passwords are not considered valid. * * Additionally, for systems using a shadow file (/etc/shadow or * INSTALL_DIR/acp_shadow), this file is checked for a valid password * and the appropriate aging is performed if at least one password entry * in the /etc/passwd file is "x". * * IN User username supplied by the user * IN Password User's password * * Return codes: * 0 - invalid username/password (or account expired) * 1 - valid username/password * 2 - valid username/password, but password to expire soon * 3 - valid username/password, but password has expired * 4 - valid username/password, but account to expire soon * * In cases 2 and 4, the global variable "daysleft" is set to the number of * days left until this event. */ int acp_native_validate(User, Password) char *User, *Password; { #ifdef _WIN32 return NTValidate(User, Password); #else struct passwd *pwd; /* storage for password entries in the password file*/ char *pw, str[ACP_LONGSTRING]; /* ACP_LONGSTRING == 80 */ int found_match = 0; /* found the entry for this user. */ #ifdef USENATIVESHADOW struct spwd *spwd; int found_x = 0; INT32 today,expire; #endif /* rewind the password file pointer (/etc/passwd) for reads */ setpwent(); /* * get the password records from /etc/passwd and * keep them in the password struct. Read till * end of file. Find the match and close file */ while ((pwd = getpwent()) != NULL) { if (pwd->pw_passwd[0] == '\0') continue; /* Disallow null passwords */ if (strncmp(pwd->pw_name, User, ACP_MAXUSTRING) != 0) continue; #ifdef USENATIVESHADOW if (strcmp("x",pwd->pw_passwd) == 0) { found_x = 1; break; } #endif /* encrypt the user provided password and match*/ pw = crypt(Password, pwd->pw_passwd); if (strncmp(pw, pwd->pw_passwd, ACP_MAXSTRING) == 0) { found_match = 1; break; } } /* close file */ endpwent(); #ifdef USENATIVESHADOW if (found_match) return 1; if (!found_x) return 0; today = DAY_NOW; /* We are unable to use setspent(), getspent() and */ /* endspent() here because they are unreliable on */ /* all platforms (escpecially SCO). The setacpsp(), */ /* getacpsp(), and endacpsp() functions do the correct */ /* thing as long as the shadow_name is set to "/etc/shadow" */ setacpsp(); /* read till end of file */ while ((spwd = getacpsp()) != NULL) { if (spwd->sp_pwdp[0] == '\0') continue; /* Disallow null passwords */ /* find the user */ if (strncmp(spwd->sp_namp, User, ACP_MAXUSTRING) != 0) continue; /* encrypt password and match */ pw = crypt(Password, spwd->sp_pwdp); if (strncmp(pw, spwd->sp_pwdp, ACP_MAXSTRING) != 0) continue; /* Check for an expired account */ #ifndef SCO if (spwd->sp_expire > 0 && today > spwd->sp_expire) break; #endif /* * We're not implementing sp_inact here to check for inactive accounts. * To do this, the time/date stamp in /usr/adm/wtmp should be checked * and validated. This would probably also require writing to this * file to update this stamp after the user logs into the Annex. */ /* Calculate password expiration date */ if (spwd->sp_lstchg > 0 && spwd->sp_max > 0) expire = spwd->sp_lstchg+spwd->sp_max; else expire = 0; /* Check for an expired password */ if (today < spwd->sp_lstchg || (expire > 0 && today > expire) || (spwd->sp_lstchg == 0)) { strncpy(old_password,spwd->sp_pwdp, sizeof(old_password)); found_match = 3; /* Check for an account that is soon to expire */ #ifndef SCO } else if (spwd->sp_expire > 0 && today >= spwd->sp_expire-spwd->sp_warn) { found_match = 4; daysleft = spwd->sp_expire-today; /* Check for a password that is soon to expire */ } /* expires today ? */ else if (expire > 0 && today >= expire-spwd->sp_warn) { found_match = 2; daysleft = expire-today; #endif } else found_match = 1; break; } /* close file */ endacpsp(); #endif return found_match; #endif /* _WIN32 */ } /* * acp_port_password_authenticate() * * This routine checks the user for port_password if the * annex has port_password options set, with the annex ip addr. * in the password file. * * Returns: * VALIDATED - If user supplied port password matches. * NOT_VALIDATED - If user supplied password doesn't match. */ acp_port_password_authenticate(Acp,logid,inet,port,ptype,service,tries) ACP *Acp; /* Handle to pass to library functions */ UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port,ptype, /* serial port number */ service; /* Expect SERVICE_CLI{,_HOOK} */ int tries; /* number of times to try */ { /* * Prompt for optional port password */ char portstring[ACP_MAX_HOSTNAME_LEN]; /* ACP_MAX_HOSTNAME_LEN == 32 */ #ifndef _WIN32 ACP_LSTRING Pass; #else /* defined _WIN32 */ ACP_STRING Pass; #endif /* defined _WIN32 */ int got_str = 1; int passed = TRUE; int blacklist = FALSE; int i; /* * look for the port-password for this port (and annex) in the * acp_passwd file. First call to acp_special converts, inet and * port to a special string and second looks for an entry in * the password file. If entry is present, prompt user for * port password and match. */ acp_special(portstring, inet, port,ptype); if (acp_getusr(portstring)) { passed = NOT_VALIDATED; /* NOT_VALIDATED == 0 */ for (i = 0; i < tries; i++) { /* prompt user for port password. */ got_str = promptstring(Acp, Pass, ACP_PORTPROMPT, 0, INPUT_TIMEOUT); if (got_str <= 0) { passed = NOT_VALIDATED; break; } else /* match the provided password with the one listed */ passed = acp_validate(portstring, Pass, &blacklist, NULL); if (passed != NOT_VALIDATED) break; } /* return appropriate value depending on pass/fail */ if (passed) return(VALIDATED); else if (got_str <= 0) return(VALIDATION_TIMED_OUT); else return(NOT_VALIDATED); } else /* no entry in the file, no port password security required. */ return(VALIDATED); } /* * This is called by routines that use acp_validate. It takes appropriate * actions based on the returned code. */ int warn_user(Acp,validp,user) ACP *Acp; /* Handle to pass to library functions */ int *validp; /* warning codes. */ char *user; /* user name */ { #if defined(USESHADOW) || defined(USENATIVESHADOW) ACP_USTRING temp,temp2; char *cp, str[ACP_LONGSTRING]; /* ACP_LONGSTRING == 80 */ int i,vcount, rv; #ifdef USE_NDBM DBM *dbm; #endif switch (*validp) { /* Password aging warning */ case 2: if (daysleft <= 0) strcpy(temp,ACP_WARNINGT); else if (daysleft == 1) strcpy(temp,ACP_WARNINGM); else sprintf(temp,ACP_WARNING,daysleft); break; /* 2 */ /* Password has expired -- prompt for new one */ case 3: /* assume that this won't work */ *validp = 0; if (user == NULL || user[0] == '\0') return -1; /* * "vcount" allows user to enter "illegal" * passwords if he's persistent. */ vcount = 0; if ((i = outputstring(Acp, ACP_EXPIRED)) != 0) return i; for (vcount = 0; vcount < 3; vcount++) { if (promptstring(Acp,temp,ACP_NEWPASS,0,INPUT_TIMEOUT) <= 0) return -1; if (temp[0] == '\0') return -1; if ((cp = test_password(temp)) != NULL) { if ((i = outputstring(Acp, cp)) != 0) return i; continue; } #if USE_NDBM if(!strcmp( old_password, temp) || (rv = matches_old_password(user, temp)) == TRUE) #else if (!strcmp(old_password, temp)) #endif { if((i=outputstring(Acp, ACP_MATCH_FOUND)) != 0) return i; else continue; } #if USE_NDBM else if(rv < 0) return 0; #endif if (promptstring(Acp,temp2,ACP_NEWPASS2,0,INPUT_TIMEOUT) <= 0) return -1; if (strcmp(temp,temp2) != 0) { if ((i = outputstring(Acp, ACP_PASSMATCH)) != 0) return i; } else break; } /* user is a moron -- kick him out */ if(vcount == 3) { if ((i = outputstring(Acp, ACP_PASS_UNCHANGED)) != 0) return i; return 0; } bzero((char*)temp2,sizeof(temp2)); cp = change_password(user,old_password,temp); if (cp != NULL) { if (*cp == ' ') strcpy(temp,cp+1); else sprintf(temp,"%s: %s.\n",cp,sys_errlist[errno]); break; } *validp = 1; #ifdef USE_NDBM rv = erpcd_lock_acp_dbm(); if (rv == 0) { sprintf(str, "%s/", install_dir); strcat(str, ACP_DBM_FILE); dbm = dbm_open(str, (O_CREAT | O_RDWR), 0600); if(dbm!=NULL) { rv = dbm_store_old_pwd(dbm, user, old_password); if (rv != 0) process_error(user, rv); dbm_close(dbm); } } dbm_unlock_acp_dbm(); #endif return 0; /* Account aging warning */ case 4: if (daysleft <= 0) strcpy(temp,ACP_AWARNINGT); else if (daysleft == 1) strcpy(temp,ACP_AWARNINGM); else sprintf(temp,ACP_AWARNING,daysleft); break; /* 3, 4 */ default: return 0; } return outputstring(Acp, temp); #endif return 0; } /* * acp_securid_authenticate() * * Prompt for password and call acp_securid_validate() to validate the * username and password. * Returns: * VALIDATED, * NOT_VALIDATED, * UNAVAILABLE - user's entry not available. */ static int acp_securid_authenticate(Acp, logid, inet, port,ptype, service, Name, tries, passcode) ACP *Acp; /* Handle to pass to library functions */ UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port,ptype, /* physical/virtual port number */ service; /* Expect SERVICE_CLI{,_HOOK} */ ACP_USTRING Name; int tries; char *passcode; /* User's passcode, NULL to prompt */ { static int got_str = 0; #ifndef _WIN32 ACP_LSTRING Pass; #else /* defined _WIN32 */ ACP_STRING Pass; #endif /* defined _WIN32 */ int passed = NOT_VALIDATED; char *usermsg = (char *)NULL; if (debug) fprintf(stderr,"acp_securid_authenticate: user %s\n",Name); /* prompt for Name and Password until valid or too many tries */ if (passcode == NULL && tries < RETRIES_MAX) { got_str = promptstring(Acp, Pass, SID_PASSPROMPT, 0, INPUT_TIMEOUT); if (got_str > 0) { /* validate user supplied password */ passed = acp_securid_validate(Name, Pass, Acp, TRUE); if (passed == UNAVAILABLE) return UNAVAILABLE; if(passed == USER_ABORT) return USER_ABORT; if (passed == VALIDATED) { /* Display appropriate permissions */ usermsg = SID_PERMGRANTD; } else { /* Display error message */ usermsg = SID_INCORRECT; } } } else if (passcode == NULL) { /* Display the appropriate error message */ if(got_str) { usermsg = SID_PERMDENIED; /* Attempts failed */ } else { usermsg = ACP_TIMEDOUT; /* Never reviced anything */ } } else /* framed user, no prompting/displaying can be done, just validate */ return(acp_securid_validate(Name, passcode, Acp, FALSE)); /* prompt user for appropriate message. */ if (usermsg) outputstring(Acp, usermsg); /* return the pass/fail/unavailable */ return(passed); } /* end of acp_securid_authenticate() */ /* * acp_securid_validate() * * Look up name and password combination by calling the Security * Dynamics Ace/Server utility. */ static int acp_securid_validate(User, Password, Acp, prompt) char *User, *Password; ACP *Acp; /* Handle to pass to library calls */ int prompt; /* TRUE if we have user prompting capability */ { int found_match = NOT_VALIDATED; #ifdef SECURID_CARD #ifdef _WIN32 static FARPROC sd_init; static FARPROC sd_check; static FARPROC sd_pin; static FARPROC sd_next; static FARPROC sd_close; static HANDLE hDllACECLNT; #endif /* _WIN32 */ int got_str = 0, i; #ifndef _WIN32 ACP_LSTRING nextcode, newpin, pinprompt; #else /* defined _WIN32 */ ACP_USTRING nextcode, newpin, pinprompt; #endif /* defined _WIN32 */ char *s; char *pintype, pinsize[20]; short flag; struct SD_CLIENT sd_dat, *sd; if (debug) printf ("acp_securid_validate\n"); #ifdef _WIN32 /* First time, load DLL */ if (hDllACECLNT == NULL) { hDllACECLNT = LoadLibrary("aceclnt.dll"); /* load DLL */ if (hDllACECLNT == NULL) { syslog (LOG_EMERG, "Error: can't load \"aceclnt.dll\"\n"); if(debug) printf ("Error: can't load \"aceclnt.dll\"\n"); return found_match; } sd_init = GetProcAddress(hDllACECLNT, "sd_init"); sd_check = GetProcAddress(hDllACECLNT, "sd_check"); sd_pin = GetProcAddress(hDllACECLNT, "sd_pin"); sd_next = GetProcAddress(hDllACECLNT, "sd_next"); sd_close = GetProcAddress(hDllACECLNT, "sd_close"); if (!sd_init || !sd_check || !sd_pin || !sd_next || !sd_close) { syslog (LOG_EMERG, "can't load function(s) from \"aceclnt.dll\"\n"); if(debug) printf ("can't load function(s) from \"aceclnt.dll\"\n"); FreeLibrary(hDllACECLNT); hDllACECLNT = NULL; return found_match; } if(debug) printf ("Functions retrieved from \"aceclnt.dll\"\n"); } #endif /* _WIN32 */ sd = &sd_dat; memset(sd, 0, sizeof(*sd)); nextcode[0] = newpin[0] = '\0'; #ifdef ACE1_2 creadcfg(); #endif /* ACE1_2 */ #if defined(ACE2_0) && !defined(_WIN32) if(creadcfg()){ if(debug) printf("error reading sdconf.rec.\n"); return found_match; } #endif /* defined(ACE2_0) && !defined(_WIN32) */ #if defined(ACE1_2) || defined(ACE2_0) if ((i=sd_init(sd)) == UNAVAILABLE) return UNAVAILABLE; else if (i) { if (debug) { printf ("failed to init client-server communications\n"); printf ("sd_init return code %d\n", i); } #ifdef _WIN32 syslog (LOG_CRIT, "%s%s%d\n", "failed to init client-server communications\n", "sd_init return code ", i); #endif /* _WIN32 */ return found_match; } #endif /* defined(ACE1_2) || defined(ACE2_0) */ if (prompt == FALSE) { /* prompting not available, short and sweet */ if (sd_check(Password, User, sd) == ACM_OK) found_match = VALIDATED; #ifdef ACE2_0 sd_close(); #endif return found_match; } switch (sd_check (Password, User, sd)) { case ACM_OK: found_match = VALIDATED; break; /* ACM_OK */ case ACM_NEXT_CODE_REQUIRED: got_str = promptstring(Acp, nextcode, SID_NEXTCODEPROMPT, 0, INPUT_TIMEOUT + 60); if (got_str < 0) { break; } switch (sd_next(nextcode, sd)) { case ACM_OK: found_match = VALIDATED; break; case ACM_ACCESS_DENIED: case ACM_NEXT_CODE_BAD: default: break; } break; /* ACP_NEXT_CODE_REQUIRED */ case ACM_NEW_PIN_REQUIRED: pintype = (sd->alphanumeric) ? SID_PINCHAR : SID_PINDIGIT; if (sd->min_pin_len == sd->max_pin_len) sprintf (pinsize, SID_PINSIZE, sd->min_pin_len); else sprintf (pinsize, SID_PINSZRANGE, sd->min_pin_len, sd->max_pin_len); #ifndef ACE2_0 if (sd->user_selectable) { sprintf (pinprompt, SID_NEWPINPROMPT, pinsize, pintype); outputstring (Acp, pinprompt); outputstring (Acp, SID_OR); } outputstring (Acp, SID_NEWPIN_2); outputstring (Acp, SID_OR); outputstring (Acp, SID_NEWPIN_3); #else /* 2.0 has new meanings for user_selectable */ sprintf(pinprompt, SID_NEWPINPROMPT, pinsize, pintype); switch(sd->user_selectable){ case CANNOT_CHOOSE_PIN: outputstring(Acp, SID_NEWPIN_2); break; case MUST_CHOOSE_PIN: outputstring(Acp, pinprompt); break; case USER_SELECTABLE: outputstring(Acp, pinprompt); outputstring(Acp, SID_OR); outputstring(Acp, SID_NEWPIN_2); break; default: break; } outputstring(Acp, SID_OR);/*or leave it in new pin mode*/ outputstring (Acp, SID_NEWPIN_3); #endif /*ACE2_0*/ /* include terminator in input string */ /* in order to distinguish between a */ /* timeout and a . */ flag = GET_TERMINATOR; got_str = promptstring_wt(Acp, newpin, " ", 0, INPUT_TIMEOUT, &flag); if (got_str < 0) /* timeout */ break; /* delete terminator from string (unless old annex)*/ if (flag == GET_TERMINATOR) { if (got_str == 0) { /* timeout */ break; } newpin[strlen(newpin) - 1] = '\0'; } if (debug) { s = &newpin[0]; printf ("---- securid_validate ---- newpin: %s\n", newpin); for (i = 0; i < (int)strlen(newpin) + 1; i++) { printf ("%02x ", s[i]); if (( (i + 1) % 16) == 0) printf ("\n"); } printf ("\n"); } if (newpin[0] == 0x04) { /* got a control-D */ sd_pin ("", CANCELLED, sd); found_match = USER_ABORT; break; } else if (strlen(newpin) == 0) { /* got a */ #ifdef ACE2_0 /* User cannot ask for a system generated pin */ if(sd->user_selectable == MUST_CHOOSE_PIN) break; #endif /* ACE2_0 */ sprintf (pinprompt, SID_SYSGENPIN, sd->system_pin); outputstring(Acp, pinprompt); if (sd_pin(sd->system_pin, 0, sd) != ACM_NEW_PIN_ACCEPTED) { break; } } #ifndef ACE2_0 else if (sd->user_selectable) #else /*ACE2_0*/ else if (sd->user_selectable != CANNOT_CHOOSE_PIN) #endif /*ACE2_0*/ { /* got a new pin */ strcpy (sd->system_pin, newpin); newpin[0] = '\0'; #ifndef _WIN32 bzero(newpin, ACP_MAXLSTRING); #else /* defined _WIN32 */ bzero(newpin, ACP_MAXUSTRING); #endif /* defined _WIN32 */ got_str = promptstring(Acp, newpin, SID_PINREENTRY, 0, INPUT_TIMEOUT); if(strcmp (sd->system_pin,newpin) != 0) { sd_pin ("", CANCELLED, sd); break; } if (sd_pin(sd->system_pin, 0, sd) != ACM_NEW_PIN_ACCEPTED) { break; } } #ifdef ACE2_0 /* user wrongly entered a newpin instead of hitting return */ else break; #endif /*ACE2_0*/ outputstring(Acp, SID_LOGNEWPIN_1); outputstring(Acp, SID_LOGNEWPIN_2); nextcode[0] = '\0'; got_str = promptstring(Acp, nextcode, SID_PASSPROMPT, 0, INPUT_TIMEOUT + 60); if (got_str < 0) break; if (sd_check (nextcode, User, sd) == ACM_OK) found_match = VALIDATED; break; /* ACP_NEW_PIN_REQUIRED */ default: case ACM_ACCESS_DENIED: break; } #endif /* SECURID_CARD */ #ifdef ACE2_0 sd_close(); #endif /* ACE2_0 */ return found_match; } /* end of acp_securid_validate() */ #if defined(ENIGMA_SAFEWORD) && !defined(_WIN32) /* * acp_safeword_printmsg() * This function prints out any messages that the SafeWord * Authentication Server wants displayed to the user. * */ static void acp_safeword_printmsg(pb, Acp) struct pblk *pb; ACP *Acp; { char instr[ACP_LONGSTRING]; /* ACP_LONGSTRING == 80 */ if (pb->msg1[0]) { sprintf(instr, "\nSafeWord: %s\n", pb->msg1); outputstring(Acp, instr); pb->msg1[0] = '\0'; } if (pb->msg2[0]) { sprintf(instr, "\nSafeWord: %s\n", pb->msg2); outputstring(Acp, instr); pb->msg2[0] = '\0'; } } #endif /* defined(ENIGMA_SAFEWORD) && !defined(_WIN32) */ #ifdef ENIGMA_SAFEWORD /* * acp_strip_hyphens() * This function strips out hyphens from a string. * */ void acp_strip_hyphens(str) char *str; { char *sp = str; do { while (*sp == '-') { sp++; } } while ( (*str++ = *sp++) != '\0'); } #endif /* ENIGMA_SAFEWORD */ #if defined(ENIGMA_SAFEWORD) && !defined(_WIN32) #ifndef NET_ENIGMA_ACP /* acp_safeword_updlogs() * Updates Safeword logs */ void acp_safeword_updlogs(pb, Acp) struct pblk *pb; ACP *Acp; { if (pb->dynpwd[0] == '\0') { strncpy(pb->dynpwd, savdyn, sizeof(pb->dynpwd)); strncpy(pb->chal, savcha, sizeof(pb->chal)); } pb->mode = UPDATE_LOGS; if(debug) (void) fprintf(stderr, "*** Calling pbmain() from acp_safeword_updlogs\n"); pbmain(pb); acp_safeword_printmsg(pb, Acp); } #endif /* not NET_ENIGMA_ACP */ /* acp_safeword_savdata() * Saves data for logging */ void acp_safeword_savdata(pb) struct pblk *pb; { if (pb->dynpwd[0]) { strcpy(savdyn, pb->dynpwd); savdyn[sizeof(pb->dynpwd) - 1] = '\0'; strcpy(savcha, pb->chal); savcha[sizeof(pb->chal) - 1] = '\0'; } } /* * acp_safeword_getfixed() * This function retrieves a fixed password, in addition to * retrieving a new fixed password if the user whants to change. * It puts the fixed password and the new fixed password into * the proper fields in the pb structure. It returns the return * value from the function promptstring() * */ int acp_safeword_getfixed(pb, Acp) struct pblk *pb; ACP *Acp; { #ifndef _WIN32 ACP_LSTRING Pass; #else /* defined _WIN32 */ ACP_STRING Pass; #endif /* defined _WIN32 */ char fp[ACP_MAXSTRING], fpv[ACP_MAXSTRING]; /* ACP_MAXSTRING == 32 */ char inistr[80]; int got_str, i; outputstring(Acp, EAS_ENTERESC); got_str = promptstring(Acp, Pass, EAS_FIXPASS, (pb->echofix == ENABLED), INPUT_TIMEOUT); if (got_str <= 0) return(got_str); strncpy(pb->fixpwd, Pass, sizeof(pb->fixpwd)); if (pb->fixpwd[0] == ESC) { got_str = promptstring(Acp, Pass, EAS_OLDFIXPASS, (pb->echofix == ENABLED), INPUT_TIMEOUT); if (got_str <= 0) return(got_str); strncpy(pb->fixpwd, Pass, sizeof(pb->fixpwd)); got_str = promptstring(Acp, Pass, EAS_NEWFIXPASS, (pb->echofix == ENABLED), INPUT_TIMEOUT); if (got_str <= 0) return(got_str); strncpy(fp, Pass, sizeof(fp)); if (fp[0]) { if (pb->echofix == DISABLED) { got_str = promptstring(Acp, Pass, EAS_REPFIXPASS, 0, INPUT_TIMEOUT); if (got_str <= 0) return(got_str); strncpy(fpv, Pass, sizeof(fpv)); } else strncpy(fpv, fp, sizeof(fpv)); for (i=0;i= pb->fixmin && strcmp(fp, pb->fixpwd) != 0) break; if (strcmp(fp, pb->fixpwd) == 0) outputstring(Acp, EAS_MUSTDIFF); else { if (strlen(fp) < pb->fixmin) { sprintf(inistr, EAS_PASSMIN, pb->fixmin); outputstring(Acp, inistr); } else outputstring(Acp, EAS_VERIFYERR); } got_str = promptstring(Acp, Pass, EAS_NEWFIXPASS, (pb->echofix == ENABLED), INPUT_TIMEOUT); if (got_str <= 0) return(got_str); strncpy(fp, Pass, sizeof(fp)); if (pb->echofix == DISABLED) { got_str = promptstring(Acp, Pass, EAS_REPFIXPASS, 0, INPUT_TIMEOUT); if (got_str <= 0) return(got_str); strncpy(fpv, Pass, sizeof(fpv)); } else strncpy(fpv, fp, sizeof(fpv)); } if (i != 3) strncpy(pb->nfixpwd, fp, sizeof(pb->nfixpwd)); else outputstring(Acp, EAS_FIXNOCHANGE); } } return(got_str); } #endif /* defined(ENIGMA_SAFEWORD) && !defined(_WIN32) */ #ifdef ENIGMA_SAFEWORD /* * acp_safeword_validate() * This function authenticates a user with the SafeWord Authentication * Server. This function takes care of all prompting. It returns a 1 * if the user passed authentication, 0 if the user faild authentication. * */ int acp_safeword_validate(Acp, logid, inet, port,ptype, service, got_str_p, User) ACP *Acp; /* Handle to pass to library calls */ UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port,ptype, /* physical/virtual port number */ service; /* Expect SERVICE_CLI{,_HOOK} */ int *got_str_p; char *User; { #ifdef NET_ENIGMA_ACP return acp_netsafeword_validate(Acp, logid, inet, port, service, got_str_p, User); #else #ifndef _WIN32 ACP_LSTRING Password; #else /* defined _WIN32 */ ACP_STRING Password; #endif /* defined _WIN32 */ struct pblk *pb; char instr[ACP_MAXUSTRING]; /* ACP_MAXLSTRING == 128 */ int i, index; char Inum[ACP_MAX_HOSTNAME_LEN], *aname; /*ACP_MAX_HOSTNAME_LEN==32*/ struct hostent *hp; FILE *fp; pb = &pblock; bzero((char*)pb, sizeof(struct pblk)); *savdyn = *savcha = '\0'; pb->mode = CHALLENGE; *got_str_p = User && strlen(User); if (*got_str_p <= 0) goto notvalid; strncpy(pb->id, User, sizeof(pb->id)); if (!pb->id[0]) goto notvalid; if(debug) (void) fprintf(stderr, "*** Calling pbmain() from acp_safeword_validate\n"); pbmain(pb); if(pb->pbresrv3 == UNAVAILABLE) return UNAVAILABLE; acp_safeword_printmsg(pb, Acp); /* * Pretend we are a valid user * because we don't want to give away * the fact that the username tried * is not in the database. */ if (pb->status == BAD_USER) outputstring(Acp, EAS_HIDEBAD); if (pb->status == PASS || pb->status == PASS_PIN) return(VALIDATED); if (pb->status != GOOD_USER && pb->status != BAD_USER) goto notvalid; range: if (pb->dynpwdf == ENABLED) { if (pb->chal[0]) { sprintf(instr, EAS_CHALLENGE, pb->chal); outputstring(Acp, instr); } *got_str_p = promptstring(Acp, Password, EAS_DYNPASS, (pb->echodyn == ENABLED), INPUT_TIMEOUT); if (*got_str_p <= 0) return(*got_str_p); acp_strip_hyphens(Password); strncpy(pb->dynpwd, Password, sizeof(pb->dynpwd)); if (pb->dynpwd[0] == ESC) pb->dynpwd[0] = '\0'; if (pb->fixpwdf == ENABLED && pb->dynpwd[0]) *got_str_p = acp_safeword_getfixed(pb, Acp); } /* mask bad user as someone who needs a fixed password */ else if (pb->fixpwdf == ENABLED || pb->status == BAD_USER) *got_str_p = acp_safeword_getfixed(pb, Acp); pb->mode = EVALUATE_ALL; pb->msg1[0] = '\0'; pb->msg2[0] = '\0'; acp_safeword_savdata(pb); /* save for logging */ if(debug) (void) fprintf(stderr, "*** Calling pbmain() from acp_safeword_validate(2)\n"); pbmain(pb); acp_safeword_printmsg(pb, Acp); /* What's going here is the user has just passed authentication and is using a token with a SOFT PIN. A SOFT PIN is a PIN that is appended to the challenge when calculating passwords. So we first ask if he wants to change his pin. But we don't want this info (the PIN) to go over the wire, so we give the challenge, and the user calculates his dynamic password with the new pin. The server then back-calculates the new pin from the dynamic password given and the challenge. */ /* Note that since the user has passed authentication, all retunrs return VALIDATED, even if the user failed to change his PIN */ if (pb->status == PASS_PIN) { *got_str_p = promptstring(Acp, Password, EAS_CHANGEPIN, 1, INPUT_TIMEOUT); if (*got_str_p <= 0 || (Password[0] != 'Y' && Password[0] != 'y')) goto valid; acp_strip_hyphens(Password); pb->mode = CHANGE_PIN; pb->dynpwd[0] = 0; pb->msg1[0] = '\0'; pb->msg2[0] = '\0'; if(debug) (void) fprintf(stderr, "*** Calling pbmain() from acp_safeword_validate(3)\n"); pbmain(pb); acp_safeword_printmsg(pb, Acp); if (pb->chal[0]) { sprintf(instr, EAS_CHALLENGE, pb->chal); outputstring(Acp, instr); } index = 0; *got_str_p = promptstring(Acp, Password, EAS_DYNPASS, (pb->echodyn == ENABLED), INPUT_TIMEOUT); if (*got_str_p <= 0) { outputstring(Acp, EAS_NOTCHANGED); goto valid; } acp_strip_hyphens(Password); strncpy(pb->dynpwd, Password, sizeof(pb->dynpwd)); if (pb->dynpwd[index] == ESC || pb->dynpwd[index] == 0 ) { outputstring(Acp, EAS_NOTCHANGED); goto valid; } /* this section of code is needed because, for some users, they only have to enter in a certain number of characters for the dynamic password. For example, the card/token may generate an eight digit password, but the user only has to enter in the first four. However, if only four characters of the dynamic password are entered here, it may not be enough to back-calculate the new SOFT PIN. Therefore, we ask for the dynamic password again and again until the whole password has been entered */ index = strlen(pb->dynpwd); for (i=0;imsg1[0] = '\0'; pb->msg2[0] = '\0'; if(debug) (void) fprintf(stderr, "*** Calling pbmain() from acp_safeword_validate(4)\n"); pbmain(pb); acp_safeword_printmsg(pb, Acp); if (pb->status == PIN_VERIFIED) { outputstring(Acp, EAS_PINCHANGED); goto valid; } if (pb->status == PIN_NOT_VERIFIED) { outputstring(Acp, EAS_NOTCHANGED); goto valid; } if (pb->status == PIN_FOUND || pb->status == PIN_NOT_FOUND) break; *got_str_p = promptstring(Acp, Password, EAS_RESTDYNPASS, (pb->echodyn == ENABLED), INPUT_TIMEOUT); if (*got_str_p <= 0) { outputstring(Acp, EAS_NOTCHANGED); goto valid; } acp_strip_hyphens(Password); strcat(pb->dynpwd, Password); if (pb->dynpwd[index] == ESC || pb->dynpwd[index] == 0 ) { outputstring(Acp, EAS_NOTCHANGED); goto valid; } index = strlen(pb->dynpwd); } if (i == RETRIES_MAX_SAFEWORD) { outputstring(Acp, EAS_NOTCHANGED); goto valid; } pb->mode = VERIFY_PIN; pb->msg1[0] = '\0'; pb->msg2[0] = '\0'; if(debug) (void) fprintf(stderr, "*** Calling pbmain() from acp_safeword_updlogs(5)\n"); pbmain(pb); acp_safeword_printmsg(pb, Acp); if (pb->status == FAIL) { outputstring(Acp, EAS_NOTCHANGED); goto valid; } if (pb->chal[0]) { sprintf(instr, "\nChallenge: %s", pb->chal); outputstring(Acp, instr); } *got_str_p = promptstring(Acp, Password, EAS_DYNPASS, (pb->echodyn == ENABLED), INPUT_TIMEOUT); if (*got_str_p <= 0) { outputstring(Acp, EAS_NOTCHANGED); goto valid; } acp_strip_hyphens(Password); strncpy(pb->dynpwd, Password, sizeof(pb->dynpwd)); if (pb->dynpwd[0] == ESC || pb->dynpwd[0] == 0 ) { outputstring(Acp, EAS_NOTCHANGED); goto valid; } pb->mode = VERIFY_PIN; pb->msg1[0] = '\0'; pb->msg2[0] = '\0'; if(debug) (void) fprintf(stderr, "*** Calling pbmain() from acp_safeword_validate(6)\n"); pbmain(pb); acp_safeword_printmsg(pb, Acp); if (pb->status == PIN_VERIFIED) { outputstring(Acp, EAS_PINCHANGED); goto valid; } outputstring(Acp, EAS_NOTCHANGED); goto valid; } if (pb->status == PASS) goto valid; /* RANGE error: the SafeWord host knows the next n dynamic passwords. If it detects a password entered that is more than n removed then it yields the RANGE error. The user is asked to enter his password again, and if it is the next one in the sequence, it lets him in */ if (pb->errcode == RANGE) goto range; acp_safeword_updlogs(pb, Acp); goto notvalid; valid: acp_safeword_updlogs(pb, Acp); return(VALIDATED); notvalid: if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, ""); if (pb->errcode) { sprintf(instr, "%s:SafeWord error #%d", pb->id, pb->errcode); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, instr); } #ifdef USE_SYSLOG if (ErpcdOpt->UseSyslog) { #ifdef USE_ANAME if (ErpcdOpt->UseHostName && (hp = gethostbyaddr(&inet, sizeof(inet), AF_INET)) != NULL && hp->h_name != NULL && hp->h_name[0] != '\0') aname = hp->h_name; else #endif { inet_number(Inum, inet); aname = Inum; } } /* UseSyslog */ #endif /*USE_SYSLOG */ if (pb->msg1[0]) { #ifdef USE_SYSLOG if (ErpcdOpt->UseSyslog && (pb->errcode == 6 || pb->errcode == 10 || pb->errcode == 12 || pb->errcode == 13 || pb->errcode ==14 || pb->errcode == 19)) { sprintf(syslogbuf, "%s:%s:[%s/%d]:%s:%s", "reject", service_name[service], aname, port,ptype, pb->id, pb->msg1); syslog(LOG_WARNING, syslogbuf); } else #endif /*USE_SYSLOG */ { sprintf(instr, "%s:%s", pb->id, pb->msg1); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, instr); } } if (pb->msg2[0]) { #ifdef USE_SYSLOG if (ErpcdOpt->UseSyslog && (pb->errcode == 6 || pb->errcode == 10 || pb->errcode == 12 || pb->errcode == 13 || pb->errcode ==14 || pb->errcode == 19)) { sprintf(syslogbuf, "%s:%s:[%s/%d]:%s:%s", "reject", service_name[service], aname, port,ptype, pb->id, pb->msg2); syslog(LOG_WARNING, syslogbuf); } else #endif { sprintf(instr, "%s:%s", pb->id, pb->msg2); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, instr); } } return(NOT_VALIDATED); #endif /* not NET_ENIGMA_ACP */ } #endif /* ENIGMA_SAFEWORD */ #if defined(ENIGMA_SAFEWORD) && !defined(_WIN32) #ifndef NET_ENIGMA_ACP /* * acp_safeword_validate_ipx() * This function authenticates a user with the SafeWord Authentication * Server. Note that a username and password already needs to be set. * It returns a 1 if the user passed authentication, 0 if the user * failed authentication. Note that authentication will fail if more * than one password is required. Authentication will also fail if a * challenge is involved. * */ int acp_safeword_validate_ipx(User, Password, Acp, logid, inet, port,ptype, service) char *User, *Password; ACP *Acp; UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port,ptype, /* physical/virtual port number */ service; /* Expect SERVICE_CLI{,_HOOK} */ { struct pblk *pb; char instr[ACP_LONGSTRING]; /* ACP_LONGSTRING == 80 */ int i, index; char Inum[ACP_MAX_HOSTNAME_LEN], *aname; /* ACP_MAX_HOSTNAME_LEN == 32 */ struct hostent *hp; pb = &pblock; bzero((char*)pb, sizeof(struct pblk)); pb->mode = CHALLENGE; strncpy(pb->id, User, sizeof(pb->id)); if (!pb->id[0]) goto notvalid; if(debug) (void) fprintf(stderr, "*** Calling pbmain() from acp_safeword_validate_ipx\n"); pbmain(pb); if (pb->status == PASS) return(VALIDATED); if (pb->status != GOOD_USER && pb->status != BAD_USER) goto notvalid; /* can't support two passwords */ if (pb->dynpwdf == ENABLED && pb->fixpwdf == ENABLED) goto notvalid; if (pb->dynpwdf == ENABLED) { acp_strip_hyphens(Password); strncpy(pb->dynpwd, Password, sizeof(pb->dynpwd)); } else if (pb->fixpwdf == ENABLED) strncpy(pb->fixpwd, Password, sizeof(pb->fixpwd)); pb->mode = EVALUATE_ALL; pb->msg1[0] = '\0'; pb->msg2[0] = '\0'; if(debug) (void) fprintf(stderr, "*** Calling pbmain() from acp_safeword_validate_ipx(2)\n"); pbmain(pb); if (pb->status == PASS) return(VALIDATED); notvalid: if (pb->errcode) { sprintf(instr, "SafeWord error #%d", pb->errcode); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, instr); } #ifdef USE_SYSLOG if (ErpcdOpt->UseSyslog) { #ifdef USE_ANAME if (ErpcdOpt->UseHostName && (hp = gethostbyaddr(&inet, sizeof(inet), AF_INET)) != NULL && hp->h_name != NULL && hp->h_name[0] != '\0') aname = hp->h_name; else #endif { inet_number(Inum, inet); aname = Inum; } } /* UseSyslog */ #endif if (pb->msg1[0]) { #ifdef USE_SYSLOG if (ErpcdOpt->UseSyslog && (pb->errcode == 6 || pb->errcode == 10 || pb->errcode == 12 || pb->errcode == 13 || pb->errcode ==14 || pb->errcode == 19)) { sprintf(syslogbuf, "%s:%s:[%s/%d]:%s:%s", "reject", service_name[service], aname, port,ptype, pb->id, pb->msg1); syslog(LOG_WARNING, syslogbuf); } else #endif { sprintf(instr, "%s:%s", pb->id, pb->msg1); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, instr); } } if (pb->msg2[0]) { #ifdef USE_SYSLOG if (ErpcdOpt->UseSyslog && (pb->errcode == 6 || pb->errcode == 10 || pb->errcode == 12 || pb->errcode == 13 || pb->errcode ==14 || pb->errcode == 19)) { sprintf(syslogbuf, "%s:%s:[%s/%d]:%s:%s", "reject", service_name[service], aname, port,ptype, pb->id, pb->msg1); syslog(LOG_WARNING, syslogbuf); } else #endif { sprintf(instr, "%s:%s", pb->id, pb->msg2); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, instr); } } return(NOT_VALIDATED); } #endif /* not NET_ENIGMA_ACP */ #endif /* defined(ENIGMA_SAFEWORD) && !defined(_WIN32) */ /* * This function is called by available() to generate an address to * include in a filter. A NULL return indicates an error. * IN token ip address with wild cards or just host name. * Returns: * IP - address (with 0's in place of asterisks) or the host name. * NULL - if "token" is erroneous. */ /* #define DEBUG_GFA 1 */ static char * get_filter_addr(token) char *token; { static char addrbuf[20]; /* If token contain an ip addr. */ if (isdigit(*token)) { register char *tp, *ap; int mask = 0, dot = 0; char mflag = FALSE; #ifdef DEBUG_GFA if (debug) fprintf(stderr, "get_filter_addr: address token \"%s\"\n",token); #endif /* Go through the token and save the values in addrbuf */ for (tp = token, ap = addrbuf; *tp; tp++, ap++) { switch (*tp) { /* * for digits and '.' of an ip addr just copy to * addrbuf and continue. */ case '.': dot++; if (!mflag) mask += 8; *ap = *tp; continue; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (mflag) { #ifdef DEBUG_GFA if (debug) fprintf(stderr, "get_filter_addr: found '%c' after wildcard\n", *tp); #endif goto addr_err; } *ap = *tp; continue; /* * wild card specified in the ip addr. * if an erroneous element return eror * otherwise put a '0' in place of the * asterisk. */ case '*': if ((*(tp+1) != '.') && (*(tp+1) != '\0')) { #ifdef DEBUG_GFA if (debug) fprintf(stderr, "get_filter_addr: found '%c' after '*'\n", *tp); #endif /* erroneous entry, return failure */ goto addr_err; } mflag = TRUE; /* complete the rest of the ip address */ strcpy(ap,"0"); ap = addrbuf + strlen(addrbuf); while (dot < 3) { strcpy(ap,".0"); ap = addrbuf + strlen(addrbuf); dot++; } continue; default: addr_err: /* return failure */ if (debug) (void)fprintf(stderr, "get_filter_addr: bad IP addr \"%s\"\n", token); sprintf(syslogbuf, "bad IP address \"%s\" in acp_restrict\n", token); syslog(LOG_ERR, syslogbuf); return(NULL); } } /* erroneous presence of dots in the incoming entry */ if (dot != 3) { #ifdef DEBUG_GFA if (debug) fprintf(stderr, "get_filter_addr: found %d dots\n", dot); #endif goto addr_err; } /* end of addr, save the mask in addrbuf */ *ap = '\0'; ap = addrbuf + strlen(addrbuf); if (mflag) { sprintf(ap, "/%d", mask); } #ifdef DEBUG_GFA if (debug) printf("get_filter_addr: %s\n", addrbuf); #endif }/* if (isdigit(*token)) */ else if (*token == '*') { /* include/exclude all addresses */ sprintf(addrbuf, "*"); } /* token is a host name eg. vulcan */ else { UINT32 addr; register unsigned char *ap = (unsigned char *)(&addr); struct hostent *hent; /* storage for host info from /etc/hosts */ #ifdef h_addr struct in_addr *iaddr; #endif #ifdef DEBUG_GFA if (debug) fprintf(stderr, "get_filter_addr: hostname token \"%s\"\n", token); #endif /* read /etc/hosts till host is found or EOF */ if ((hent = gethostbyname(token)) == NULL) { if (debug) (void)fprintf(stderr, "get_filter_addr: bad hostname \"%s\"\n", token); sprintf(syslogbuf, "could not resolve hostname \"%s\" in acp_restrict\n", token); syslog(LOG_ERR, syslogbuf); return(NULL); } /* save the ip address. */ #ifdef h_addr iaddr = (struct in_addr *)(*hent->h_addr_list); addr = iaddr->s_addr; #else addr = hent->h_addr->s_addr; #endif sprintf(addrbuf, "%u.%u.%u.%u", *ap, *(ap+1), *(ap+2), *(ap+3)); } #ifdef DEBUG_GFA if (debug) fprintf(stderr, "get_filter_addr: returning \"%s\"\n", addrbuf); #endif return(addrbuf); } /********************************************************************* * available * Process restriction information on available ports * IN env_p pointer to environment structure * IN rinet internet address of remote host * IN port destination port * IN do_filters TRUE to construct filters, FALSE otherwise * OUT filter_pp address of filter_list pointer * Results: Access status (ACCESS_RESTRICTED or ACCESS_UNRESTRICTED) */ available(env_p, rinet, port,ptype, do_filters, filter_pp) struct environment_spec *env_p; UINT32 rinet; int port,ptype; int do_filters; STR_LIST **filter_pp; { int done = 0; int got_annex = 0; int filter_dflt = 0; int status; char token[TOKEN_SIZE]; /*using the same length as in */ /*acp_regime parser. A larger */ /*array is required since */ /*profile criteria gets */ /*chopped with SIZE_INET */ char *host_p = NULL; char *ports_p = NULL; struct environment_values tmp_val; /* * Scan file until EOF, or until a match is encountered. In the * event of no match (EOF), take default action - allow access. */ while(!done) { status = get_string(token); /* * Print a debug message saying what token we got. The * switch statement will contain another printf() telling * the status in a printable form. */ if(debug) (void) fprintf(stderr, "available: get_string returns token %s, status: ", token); switch(status) { case END_OF_FILE: /* Print status if in debug mode */ if(debug) (void) fprintf(stderr, "EOF\n"); done = -1; break; /* END_OF_FILE */ case IS_ANNEX: /* Print status if in debug mode */ if(debug) (void) fprintf(stderr, "IS_ANNEX\n"); /* Clear out the environment structure */ (void) bzero((char*)&tmp_val, sizeof(struct environment_values)); /* Stick the token into the environment values struct */ (void) strcpy(tmp_val.annex, token); if(match_env_options(env_p, &tmp_val)) { got_annex = EXCLUDE_LIST; if(debug) (void) fprintf(stderr, "MATCH(exclude)!\n"); } else { got_annex = 0; if(debug) (void) fprintf(stderr, "No match (exclude)\n"); } break; /* IS_ANNEX */ case IS_ANNEX_NOT: /* Print status if in debug mode */ if(debug) (void) fprintf(stderr, "IS_ANNEX_NOT\n"); /* Clear out the environment structure */ (void) bzero((char*)&tmp_val, sizeof(struct environment_values)); /* Stick the token into the environment values struct */ (void) strcpy(tmp_val.annex, token); if(match_env_options(env_p, &tmp_val)) { got_annex = INCLUDE_LIST; if(debug) (void) fprintf(stderr, "MATCH(include)!\n"); } else { got_annex = 0; if(debug) (void) fprintf(stderr, "No match (include)\n"); } break; /* IS_ANNEX_NOT */ case IS_ENV_STRING: /* Print status if in debug mode */ if(debug) (void) fprintf(stderr, "IS_ENV_STRING\n"); /* Clear out the environment structure */ (void) bzero((char*)&tmp_val, sizeof(struct environment_values)); /* * Try and parse the environment. If that works, * see if we get a match. */ if(env_keyword_routine(token, &tmp_val) >= 0) { if(match_env_options(env_p, &tmp_val)) got_annex = EXCLUDE_LIST; else got_annex = 0; } else { syslog(LOG_ERR, "available: environment string parse failed.\n"); return(ACCESS_RESTRICTED); } break; /* IS_ENV_STRING */ case IS_ENV_STRING_NOT: /* Print status if in debug mode */ if(debug) (void) fprintf(stderr, "IS_ENV_STRING_NOT\n"); /* Clear out the environment structure */ (void) bzero((char*)&tmp_val, sizeof(struct environment_values)); /* * Try and parse the environment. If that works, * see if we get a match. */ if(env_keyword_routine(token, &tmp_val) >=0 ) { if(match_env_options(env_p, &tmp_val)) got_annex = INCLUDE_LIST; else got_annex = 0; } else { syslog(LOG_ERR, "available: environment string parse failed.\n"); return(ACCESS_RESTRICTED); } break; /* IS_ENV_STRING_NOT */ case IS_HOST: /* Print status if in debug mode */ if(debug) (void) fprintf(stderr, "IS_HOST\n"); /* * Only bother parsing this if we've got an annex * or environment string we've matched already. */ if(got_annex) { /* * Parse the host/ports specification and find * out whether we got a host only, a host and * port description, or an error. */ if(get_host_and_ports(token, &host_p, &ports_p) == FOUND_HOST_ERROR) { syslog(LOG_ERR, "available: get_host_and_ports failed.\n"); if(debug) (void) fprintf(stderr, "available: get_host_and_ports failed.\n"); return(ACCESS_RESTRICTED); } if(debug) (void) fprintf(stderr, "available: host: %s, ports: %s\n", (host_p == NULL)?"NULL":host_p, (ports_p == NULL)?"NULL":ports_p); /* * Filter construction algorithm (simple version; could be smarter) * * If this is a restrict (:) entry, then we want to build incl filters; * if we're already building excl filters, that's an error. * * If this is an allow (~) entry, then we want to build excl filters * (which presumes the default is deny because the filters make it so); * if we're already building incl filters, that's an error. * * NOTE: this doesn't handle ports because we'd need a seperate * filter for each port. We need to make the filter command * smarter to do this correctly. */ if (do_filters) { char *addrp; char *iep, *oep, *pp; char buffi[60], buffo[60]; STR_LIST *slp; /* if a range of ports is specified, deny access */ if (ports_p) { if (index(ports_p, '-') != NULL) { if (debug) (void)fprintf(stderr, "available: port range on filters\n"); syslog(LOG_ERR, "cannot generate filters for port ranges"); return(ACCESS_RESTRICTED); } if ((pp = index(ports_p, ',')) != NULL) *pp++ = '\0'; } /* if (ports_p) */ else pp = NULL; if (got_annex == EXCLUDE_LIST) { if (filter_dflt == EXCLUDE_LIST) { if (debug) (void)fprintf(stderr, "available: incl/excl mismatch\n"); syslog(LOG_ERR, "acp_restrict incl/excl mismatch\n"); return(ACCESS_RESTRICTED); } filter_dflt = INCLUDE_LIST; if ((addrp = get_filter_addr(host_p)) == NULL) return(ACCESS_RESTRICTED); sprintf(buffi, "input include ip addr %s *", addrp); sprintf(buffo, "output include ip addr %s *", addrp); } /* if (got_annex == EXCLUDE_LIST) */ else { if (filter_dflt == INCLUDE_LIST) { if (debug) (void)fprintf(stderr, "available: excl/incl mismatch\n"); syslog(LOG_ERR, "acp_restrict excl/incl mismatch\n"); return(ACCESS_RESTRICTED); } filter_dflt = EXCLUDE_LIST; if ((addrp = get_filter_addr(host_p)) == NULL) return(ACCESS_RESTRICTED); sprintf(buffi, "input exclude ip addr %s *", addrp); sprintf(buffo, "output exclude ip addr %s *", addrp); }/* else, if (got_annex == EXCLUDE_LIST) */ iep = buffi + strlen(buffi); oep = buffo + strlen(buffo); filter_next_port: if (ports_p) { char buffp[20]; sprintf(buffp, " port %s * disc", ports_p); strcat(buffi, buffp); strcat(buffo, buffp); } else { strcat(buffi, " disc"); strcat(buffo, " disc"); } if (debug) (void)fprintf(stderr, "available: generated \"%s\"\n",buffi); /* do the output filter */ slp = racp_create_strlist(buffo, strlen(buffo)); if (slp == NULL) { if (debug) (void)fprintf(stderr, "available: cannot create SRT_LIST\n"); syslog(LOG_ERR, "could not generate output filter entry"); return(ACCESS_RESTRICTED); } slp->next = *filter_pp; *filter_pp = slp; /* do the input filter */ slp = racp_create_strlist(buffi, strlen(buffi)); if (slp == NULL) { if (debug) (void)fprintf(stderr, "available: cannot create SRT_LIST\n"); syslog(LOG_ERR, "could not generate input filter entry"); return(ACCESS_RESTRICTED); } slp->next = *filter_pp; *filter_pp = slp; if ((ports_p = pp) != NULL) { if ((pp = index(pp, ',')) != NULL) *pp++ = '\0'; *iep = '\0'; *oep = '\0'; goto filter_next_port; } }/* if (do_filters) */ else { /* Match against the host and ports - return * the access restriction if we win! */ if(wild_match(token, rinet) && match_host_ports(port, ports_p)) { /* if debug, report that we succeeded */ if (debug) (void)fprintf(stderr, "available: MATCH! Access is %s\n", (got_annex == EXCLUDE_LIST) ? "RESTRICTED" : "UNRESTRICTED"); return((got_annex == EXCLUDE_LIST) ? ACCESS_RESTRICTED : ACCESS_UNRESTRICTED); } } /*else , if (do_filters) */ }/* if (got_annex) */ break; /* IS_HOST */ case IS_ENV_STRING_ERR: if(debug) (void) fprintf(stderr, "IS_ENV_STRING_ERR\n"); syslog(LOG_ERR, "available: environment string parse failed. \n"); return ACCESS_RESTRICTED; default: if(debug) (void) fprintf(stderr, "*** UNRECOGNIZED TOKEN ***\n"); break; }/* switch (status) */ }/* while (!done) */ /* End of file - no explicit restriction found - include me */ if(debug) (void) fprintf(stderr, "available: No match - unrestricted.\n"); return(ACCESS_UNRESTRICTED); } /* * get_string() - low level parser for acp_restrict file * OUT String profile criteria or the annex for which the following * annex/host is restricted or allowed. * Returns: * END_OF_FILE * IS_ENV_STRING - this env_string restricts the following annex/host. * IS_ENV_STRING_NOT - this env_string does not restrict the following annex/host. * IS_ANNEX - this annex/host restricts the following annex/host. * IS_ANNEX_NOT - this annex/host does not restricts the following annex/host. */ get_string(String) char *String; { static int start_file = -1, end_file = 0, incomment; static FILE *rfile; char restrict_pth[PATHSZ]; int slen = 0, quotes_count=0, value; int is_env_string = 0, inquotes = 0; /* Set if we have an env str */ int can_contain_whitespace = 0; /* true if whitespace OK */ int can_contain_comma = 0; /* * Set if parsing an expression that * can contain a comma, such as: * host[24,45] */ int can_contain_colon = 0; /* Time specifications can contain colon */ /* macro saves the acp_restrict filepath. */ ACP_RESTRICT(restrict_pth); /* read file */ if(start_file) if(!(rfile = fopen(restrict_pth, "r"))) return END_OF_FILE; start_file = 0; incomment = 0; while(!end_file) { value = getc(rfile); /* * fall through for the white space and save the token * in the String array. If a colon (:) or tilde (~) is hit, * return restrict and not-restrict respectively. */ switch(value) { case EOF: end_file = -1; /* * Check and see if this token we're processing * can contain whitespace. If it can, just add * the character to the buffer, and go around for the * the next. * * The default action is to drop through and use * the whitespace as a delimiter to signify the * end of the token. */ if(can_contain_whitespace && (slen < (TOKEN_SIZE - 1))) { String[slen++] = value; break; } /* FALL THROUGH - if whitespace not allowed */ case '\r': case '\n': case '\f': case '\0': incomment = 0; case ' ': case '\t': case ',': /* * Check and see if this token we're processing * can contain commas. If it can, just add * the character to the buffer, and go around for the * the next. * * The default action is to drop through and use * the comma as a delimiter to signify the * end of the token. */ if(can_contain_comma && (slen < (TOKEN_SIZE - 1))) { String[slen++] = value; break; } /* FALL THROUGH - if comma not allowed */ case '\\': if(slen) { String[slen] = '\0'; return IS_HOST; } break; /* '\\', ',', '\t', ' ' , '\0', '\f', '\n', '\r', EOF */ case '#': incomment = 1; break; /* # */ case '"': if (!incomment) { /* if we're in a comment, who cares what we find! */ inquotes = !inquotes; quotes_count++; if(quotes_count > 2) /*this should be changed if other security profiles allow quotes */ return IS_ENV_STRING_ERR; } if(!incomment && slen){ String[slen++] = value; if(inquotes) can_contain_colon = 1; else can_contain_colon = 0; } break; case ':': if(!can_contain_colon){ if(!incomment && slen) { String[slen] = '\0'; /* This might be an environment string or * a host specification. */ if(is_env_string) return IS_ENV_STRING; else return IS_ANNEX; } } else String[slen++] = value; break; /* ':' */ case '~': if(!incomment && slen) { String[slen] = '\0'; /* This might be an environment string or * a host specification. */ if(is_env_string) return IS_ENV_STRING_NOT; else return IS_ANNEX_NOT; } break; /* '~' */ case '=': /* * If we see an equal sign, we're parsing an * an environment string. We need to set a flag * to indicate this, and we also need to say that * commas/whitespace can be included in the specification * (as opposed to functioning as a delimiter). */ is_env_string = 1; can_contain_comma = 1; can_contain_whitespace = 1; /* * FALL THROUGH - we need to put the equal * sign into the parsed token. */ case '[': case ']': /* * For the braces, we need to turn on or off the * appropriate indicators to say if whitespace/commas * are allowed or not. * * In either case, fall through and add the characters * to the result string. * * Perhaps it would be better do duplicate the code * that puts them in the buffer. */ if(value == '[') { can_contain_comma = 1; can_contain_whitespace = 1; } else if(value == ']') { can_contain_comma = 0; can_contain_whitespace = 0; } /* FALL THROUGH */ default: /* Add the character to the string */ if(!incomment && slen < (TOKEN_SIZE - 1)) String[slen++] = value; } } (void)fclose(rfile); return END_OF_FILE; } /* * secure_cache() * * Create internal tables for annex encryption keys. * A separate table is created for wildcard entries. * Bytes containing nulls match corresponding byte of internet address. * * This routine is invoked at initialization time, or by a SIGHUP. */ #define GOT_END 1001 #define GOT_ANNEX 1002 #define LAST_ANNEX 1003 #define GOT_PASSWORD 1004 #define SIZE_TOKEN 64 #define ANNEX_LIST 2501 #define ANNEX_PASSWORD 2502 void secure_cache() { int done = 0; /* set on end of file */ int status; /* return from get_token() */ int wildcard; /* is token a wildcard? */ int i; /* use me for loops */ int wildmark = 0; /* mark wildcard cache */ int tamemark = 0; /* mark regular cache */ int quest; /* which token is expected? */ UINT32 address; /* internet address */ KEYDATA *key; /* pointer to crypt table */ char token[SIZE_TOKEN + 1]; /* token from get_token() */ FILE *sfile; /* file pointer */ char keypath[PATHSZ]; /* * Free any previously allocated KEYDATA tables, since this * routine is also called on a SIGHUP and tables are replaced */ for(i = 0; i < Nmalloced; i++) free(Allocated[i]); Nwild = Ntame = Nmalloced = 0; ACP_KEYS(keypath); sfile = fopen(keypath, "r"); if(!sfile) done = -1; quest = ANNEX_LIST; while(!done) { status = get_token(sfile, quest, token, &wildcard); switch(status) { case LAST_ANNEX: quest = ANNEX_PASSWORD; case GOT_ANNEX: if(!strlen(token)) address = 0; else address = inet_address(token); if(wildcard) { if(Nwild < MAX_WILD) Wild[Nwild++].addr = address; } else if(address) { if(Ntame < MAX_TAME) Tame[Ntame++].addr = address; } break; case GOT_END: done = -1; case GOT_PASSWORD: token[KEYSZ - 1] = '\0'; /* limit size */ if(Nmalloced < MAX_KEYS) { key = make_table(token, (KEYDATA *)0); Allocated[Nmalloced++] = (char *)key; while(wildmark < Nwild) Wild[wildmark++].ckey = key; while(tamemark < Ntame) Tame[tamemark++].ckey = key; } quest = ANNEX_LIST; break; default: break; } } if(debug) { printf("Wild entries:\n"); for(i = 0; i < Nwild; i++) printf("Address %8.8x, key <%s>\n", Wild[i].addr, Wild[i].ckey->password); printf("Tame entries:\n"); for(i = 0; i < Ntame; i++) printf("Address %8.8x, key <%s>\n", Tame[i].addr, Tame[i].ckey->password); } if(sfile) (void)fclose(sfile); return; } /* * get_token() - low level parser for acp_keys file * Returns: * GOT_PASSWORD * GOT_ANNEX * LAST_ANNEX * GOT_END * ANNEX_PASSWORD */ get_token(file, quest, String, wild) FILE *file; /* file pointer for ACP_KEYS file */ int quest; /* quest is for an Annex address or a password? */ char *String; /* token - Annex identifier or password */ int *wild; /* is this token a wildcard entry? */ { int slen = 0, /* length of current token */ endoffile = 0, /* reached end of file */ value; /* returned character or EOF */ *wild = 0; String[0] = '\0'; while(!endoffile) { value = getc(file); switch(value) { case EOF: String[slen] = '\0'; /* can be null */ endoffile = -1; case ' ': case '\t': break;/* skip white space */ case '\\': value = getc(file); if(value == EOF) /* cant escape EOF */ endoffile = -1; else if(value == '\n') /* ignore newlines */ break; else goto data; /* else it is data */ case '#': /* skip from # to end of line */ while((value = getc(file)) != '\n' && value != EOF) ; case '\n': String[slen] = '\0'; /* can be null */ return GOT_PASSWORD; /* got the password */ case ':': if(quest == ANNEX_PASSWORD) goto data; String[slen] = '\0'; /* null terminate */ return LAST_ANNEX; /* end of list */ case ',': case ';': if(quest == ANNEX_PASSWORD) /* can be in passwd */ goto data; String[slen] = '\0'; /* null terminate */ return GOT_ANNEX; /* got an Annex */ case '*': *wild = -1; /* got a wildcard */ value = '0'; /* treat as zero */ data: default: if(slen < (SIZE_TOKEN - 1)) /* token character */ String[slen++] = value; }/* switch(value) */ }/* while (!endoffile) */ return GOT_END; } /* * annex_key() * * Retrieve key from internal address/key cache. * This information is derived from ACP_KEYS file, at start or SIGHUP. */ KEYDATA * annex_key(iaddr) UINT32 iaddr; /* internet address of an Annex */ { int i, j; /* indices for loops */ char *x, y[4]; /* point to entry, copy of iaddr */ /* search exact match table first, then search wildcard table */ for(i = 0; i < Ntame; i++) /* straight comparison */ { if(Tame[i].addr == iaddr) return Tame[i].ckey; } for(i = 0; i < Nwild; i++) /* wildcard comparison */ { x = (char *)&Wild[i].addr; bcopy((char *)&iaddr, y, sizeof(iaddr)); for(j = 0; j < sizeof(iaddr); j++) if(x[j] == '\0') y[j] = '\0'; if(Wild[i].addr == *(UINT32 *)y) return Wild[i].ckey; } return (KEYDATA *)0; } /* * Look for a special port password for this port. * These are stored in the acp_passwd file as inetaddr.port, i.e.,: * for milo (132.245.1.200) port 13: * 132.245.1.200.13:xxxxxxxxxxxxx: */ void acp_special(Special, inet, port,ptype) char *Special; UINT32 inet; int port,ptype; { unsigned char *Pinet = (unsigned char *)&inet; /* * Convert internet address and port number to special string */ (void)sprintf(Special, "%u.%u.%u.%u.%u", Pinet[0], Pinet[1], Pinet[2], Pinet[3], port); return; } /* * This fxn looks for a user's entry in the acp_passwd file. * Returns: * 1/0 - based on the success of failure of the search. */ acp_getusr(Name) char *Name; /* username */ { #ifndef _WIN32 struct passwd *pwd; /* * Check password file for an entry */ setacppw(NULL); /*6131*/ while(pwd = getacppw()) { if(!strcmp(pwd->pw_name, Name)) return 1; } endacppw(); #endif return 0; /* no entry found */ } /* * check_port_pool() returns TRUE if the port/Annex pair belongs to * "pool_name" as defined in the file acp_userinfo. * When no "pool_name" is specified all pools are checked. * returns DIAL_SUCC if pool entry was found. */ int check_port_pool(annex, port,ptype, pool_name) UINT32 annex; int port,ptype; char *pool_name; { int error; /* returns the results of the search */ if(debug) { if(pool_name != 0) printf("annex=0x%x, port=%d, pool_name=%s.\n",annex, port, pool_name); else printf("annex=0x%x, port=%d, pool_name=0.\n",annex, port); } /* check pool/ports database for this annex and port */ error = get_pool_entry_by_addr(pool_name, annex, port,ptype); if(debug) printf("check_port_pool: error=%d\n",error); /* return the results found */ return(error); } /* * access_code_validate() scans the userinfo database for * "Name" and then "Access_code". Once match is found, it fills in * the user profile arguments: the telephone number, the port mask, * the inbound and the outbound modem pool names. * returns TRUE if username/accesscode was found, FALSE otherwise. * IN Name * IN Access_code provided by the user (in check_dialback()). * OUT Job clicommand specified with accesscode info * block in userinfo. * OUT In_pool port&annex pool for inbound requests. * OUT Out_pool port&annex pool for outbout requests. * OUT Phone phone numbers found from the userinfo entry. * Return: * TRUE/FALSE - depending on the results of the search. */ int access_code_validate(Name, Access_code, Phone, Job,In_pool, Out_pool, ipx_phone, envinfo) char *Name; char *Access_code; char *Phone; char *Job; char *In_pool; char *Out_pool; char *ipx_phone; struct env_gr_info *envinfo; /* * user's environemnt and group file info * required since now we support profile * criteria in acp_userinfo file */ { int error; Access ac_info; int len, index, phone_index; char *phone_str = ""; struct _phone *_phone_ptr = (struct _phone *)NULL; *Phone = '\0'; if (debug) { printf("access_code_validate: name=%s acc=%s\n", Name, Access_code); if (ipx_phone) { printf(" ipx_phone = %s", ipx_phone); } printf("\n"); } /* * search the database for username (Name) * and Access_code,if match found keep the * info. in ac_info struct. The new arg. "envinfo" * contains the user's environment and group file * info which is required for profile criteria * matching. */ error = get_user_access(Name, Access_code, &ac_info, envinfo); /* * If debug mode is on and search is successful , print * found info on the debug output . */ if (debug) { printf("get_user_access ret %d\n",error); /* found match , print info */ if (error == DIAL_SUCC) { int i = 1; printf("... code:\t%s.\n",ac_info.ac_code); printf("... phone #'s:"); for (_phone_ptr = ac_info.ac_phone_list; _phone_ptr; _phone_ptr = _phone_ptr->next,i++) { if (((i % 3) == 1) && (i != 1)) printf("\n\t"); printf("\t\"%s\" ", _phone_ptr->ac_phone); } printf("\n"); printf("... inpool:\t%s.\n",ac_info.ac_inpool); printf("... outpool:\t%s.\n",ac_info.ac_outpool); printf("... job:\t%s.\n",ac_info.ac_job.j_string); } } /* no match found, return false. */ if (error != DIAL_SUCC) return (FALSE); /* for ipx request. */ if (ipx_phone) { /* * This is for IPX. */ int i; len = strlen(ipx_phone); if (index = ((len <= 2) ? 1 : 0)) phone_index = atoi(ipx_phone); if (debug) printf("ipx_phone = %s index = %d\n", ipx_phone, index); /*save the phone values that match in phone_str */ for (i = 0, _phone_ptr = ac_info.ac_phone_list; (!*phone_str) && _phone_ptr; _phone_ptr = _phone_ptr->next) { if (index) { if (++i == phone_index) { phone_str = _phone_ptr->ac_phone; } continue; } if (!(strcmp(_phone_ptr->ac_phone, IPX_CHARGE_BACK_TOK))) { phone_str = ipx_phone; continue; } else if (!strncmp(ipx_phone, _phone_ptr->ac_phone, strlen(_phone_ptr->ac_phone))) { phone_str = _phone_ptr->ac_phone; } } } else { /* * This is for CLI. */ if (ac_info.ac_phone_list) phone_str = ac_info.ac_phone_list->ac_phone; } /* * IPX accesscode entries must have a phone number. */ if (ipx_phone && !*phone_str) { return (FALSE); } if (debug) printf("access_code_validate: obtained phone=%s\n", phone_str); /* save the info. found from the userinfo entry */ (void)strcpy(Phone, phone_str); (void)strcpy(Job, ac_info.ac_job.j_string); (void)strcpy(In_pool, ac_info.ac_inpool); (void)strcpy(Out_pool,ac_info.ac_outpool); return(TRUE); } /* * check_dialback() is called by port_to_annex() on the host to * determine if this is a valid dialback request, in which case * the user is prompted for the access code and (if all goes * well) the dialback request is started. * * This is what we are trying to do: * * * go through the username and password authentication * if (this Annex port number not included in any pool) * { * this is a direct connect * } * else * { * prompt for Access_Code * if (bad access code) * goto reject_request * else if (port not member if inbound pool) * goto reject_request * else if (outbound pool name not specified) * this is a direct connect * else if (telephone number not specified) * prompt user for telephone number * if (telephone number not given) * goto reject_request * else * dial back * } * */ void check_dialback(Acp, logid, inet, port,ptype, service,Name) ACP *Acp; /* Handle to pass to library functions */ UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port,ptype, /* physical/virtual port number */ service; /* Expect SERVICE_CLI */ char *Name; { #ifndef _WIN32 ACP_LSTRING Access_code,Phone; #else /* defined _WIN32 */ ACP_STRING Access_code,Phone; #endif /* defined _WIN32 */ char Job[LEN_JOB], Port_mask[LEN_PORT_MASK]; char In_pool[ACP_LONGSTRING], Out_pool[ACP_LONGSTRING]; char Message[TOKEN_SIZE]; int retries, passed; int got_str; UINT32 target_inet, ret_err_code = REJECT_CODE(CODE_UNKNOWN, REJ_ERPCDDENY); UINT32 mask = CLI_MASK, rcode = REQ_DENIED; int dialb_port=port; int dialb_ptype = ptype; PoolEntry pool_info; int error; Uprof up; struct environment_spec *env_p; struct env_gr_info envinfo; Message[0] = 0; /* empty string */ /* Initialize the various uprof structs */ (void) bzero((char*)&up, sizeof(Uprof)); /* creating the user environment. */ if((env_p = create_env()) == NULL) { /* failed to create environment for the user; deny access */ port_to_annex_authorize(Acp, REQ_DENIED, 0xffffffff, Name,0); syslog(LOG_ERR, "Failed to allocate memory"); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, Message); if (debug) { printf("check_dialback log: %s.\n",Message); fflush(stdout); } return; }/* failed to create env */ env_p->annex = inet; /* ip addr. of annex requesting authentication*/ env_p->port = port; /* port no. where request is generated from */ env_p->ptype = ptype; env_p->protocol = service; /* cli, slip, ppp ?? */ env_p->regime = (struct security_regime *)NULL; /* what regime used for authe.*/ env_p->group_list = (struct group_entry *)NULL; /* groups that user belogns to*/ /* Get the timestamp for this action */ if (get_time_stamp(&(env_p->time)) == FALSE) { /* Log this event */ syslog(LOG_ERR,"Failed to retrieve system time"); } /* save username in the environment. */ strncpy(env_p->username, Name, LEN_USERNAME -1); env_p->username[LEN_USERNAME -1]='\0'; /* * userinfo database doesn't exist due to a corrupt acp_userinfo file, * this is a security breach and all users must be denied access and * event logged. */ if(deny_all_users) { syslog(LOG_ERR, "userinfo database is corrupted, denying access to\ all users. Check acp_userinfo file!\n"); (void)outputstring(Acp, ACP_PERMDENIED); port_to_annex_authorize(Acp, REQ_DENIED, 0xffffffff, Name, 0); if(ISUDP(Acp->state)) log_message(inet, logid, port, ptype,service, EVENT_REJECT, Message); return; } /* user's environment and group file info is saved. */ envinfo.gr_info = &fileinfo; envinfo.env = &env_p; /* * get access code from the user. Once the access code * is received , call access_code_validate() which * searches for that access code definition in the user's * userinfo entry. If the access code is matched, * "Phone", "Job", " In_pool " and " Out_pool " information * is saved. This is repeated until a workible "Access_code" * is provided by the user and found in userinfo database * or "retries < 3" */ for (retries = 0; retries < RETRIES_MAX; retries++) { passed = FALSE; got_str = promptstring(Acp, Access_code, ACP_ACCESSCODEPROMPT, 0, INPUT_TIMEOUT); if (got_str < 0) continue; if (!got_str) { passed = FALSE; break; } else { passed = access_code_validate(Name, Access_code, Phone, Job, In_pool, Out_pool, 0, &envinfo); } if (passed) break; } /* * If no access code is provided or no match is found * in the userinfo database, deny this user. */ if ( ! passed ) { (void)outputstring(Acp, ACP_PERMDENIED); port_to_annex_authorize(Acp, REQ_DENIED, 0xffffffff, Name, 0); (void) strcat ( Message, (char *)Name ) ; (void) strcat ( Message, ",bad access code" ); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, Message); if (debug) { printf("check_dialback log: %s.\n",Message); fflush(stdout); } } else /*match found, proceed. */ { /* check if port belongs to inbound pool */ if (check_port_pool(inet, port,ptype, In_pool) != DIAL_SUCC) { /* request came from non authorized port: reject it */ (void)outputstring(Acp, ACP_PERMDENIED); port_to_annex_authorize(Acp, REQ_DENIED, 0xffffffff, Name, 0); (void) strcat ( Message, (char *)Name ) ; (void) strcat ( Message, ",bad inbound port"); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, Message); if (debug) { printf("check_dialback log: %s.\n",Message); fflush(stdout); } goto finished; } else if (strlen(Out_pool) == 0) { /* outbound poolname undefined: this is a direct connect */ (void)outputstring(Acp, ACP_PERMGRANTD); /* * This call looks up userinfo database and chooses * the first entry that it finds and returns a pointer to the * entry. If the search fails, ie nothing for this user is found, * pointer points to an emtpy Uprof struct. */ error = get_user_profile_entry(&up, Name, &env_p, &fileinfo); /* If user has a deny entry in userinfo entry, deny access. */ if(up.up_deny) { /* set the appropriate error code for logging */ ret_err_code = REJECT_CODE(CODE_UNKNOWN,REJ_DENYUSER); (void)outputstring(Acp, ACP_PERMDENIED); /* send the authorize packet packet and log */ port_to_annex_authorize(Acp, ret_err_code, 0xffffffff, Name, 0); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, ret_err_code, Message); release_env(env_p); release_uprof(&up); return; } /* save climasks specified in userinfo entry, */ if (error == ACPU_ESUCCESS && up.up_climask != 0) mask = up.up_climask; if (service == SERVICE_CLI_HOOK && up.up_cmd_list) { Acp->auth.hmask = (CHOOK_PROMPTING | CHOOK_BADCMND | CHOOK_GOODCMND); Acp->auth.cmd_list = up.up_cmd_list; rcode = REQ_GRANT_HOOK; } else rcode = REQ_GRANTED; #ifdef DIALUP_SLIP set_dialup_slip(Acp, inet, port,ptype, Name); #endif if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_LOGIN, Name); if (debug) { printf("check_dialback log: %s.\n",Name); fflush(stdout); } error = port_to_annex_authorize(Acp,rcode,mask,Name,Acp->auth.hmask); if (debug) fprintf(stderr,"acp_policy.c: called port_to_annex_authorize;\ returned value error = %d\n",error); goto finished; } /* port not a member of outbound pool */ else if (get_port_pool(Out_pool, &pool_info) != ACPU_ESUCCESS) { /* Unable to read pool information: probably an invalid */ /* pool name, or data base problem: reject request */ (void)outputstring(Acp, ACP_PERMDENIED); port_to_annex_authorize(Acp, REQ_DENIED, 0xffffffff, Name,0); (void) strcat ( Message, (char *)Name ) ; (void) strcat ( Message, ",bad outbound poolname"); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, Message); if (debug) { printf("check_dialback log: %s.\n",Message); fflush(stdout); } goto finished; } /* telephone number not specified */ else if (strlen(Phone) == 0) { /* prompt user for telephone number */ got_str = promptstring(Acp, Phone, ACP_PHONEPROMPT, 1, INPUT_TIMEOUT); if (debug) { printf("check_dialback: got_str=%d, phone=%s.\n", got_str,Phone); fflush(stdout); } } /* telephone number not provided. */ if (strlen(Phone) == 0) { /* error: don't have the phone number */ (void)outputstring(Acp, ACP_PERMDENIED); port_to_annex_authorize(Acp, REQ_DENIED, 0xffffffff, Name,0); (void) strcat ( Message, (char *)Name ) ; (void) strcat ( Message, ",no phone number"); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, Message); if (debug) { printf("check_dialback log: %s.\n",Message); fflush(stdout); } } else { /* everything looks good */ (void)outputstring(Acp, ACP_DIALBACKGRANTD); bzero((char*)&up, sizeof(Uprof)); /* * This call looks up userinfo database and chooses * the first entry that it finds and returns a pointer to the * entry. If the search fails, ie nothing for this user is found, * pointer points to an emtpy Uprof struct. */ error = get_user_profile_entry(&up, Name, &env_p, &fileinfo); /* If user has a deny entry in userinfo entry, deny access. */ if(up.up_deny) { /* set the appropriate error code for logging. */ ret_err_code = REJECT_CODE(CODE_UNKNOWN,REJ_DENYUSER); /* print perm. denied message for the user. */ (void)outputstring(Acp, ACP_PERMDENIED); /* set the authorize packet. and log */ port_to_annex_authorize(Acp, ret_err_code, 0xffffffff, Name,0); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, ret_err_code, Message); release_env(env_p); release_uprof(&up); return; } /* send "granted" packet to annex with appropriate climasks */ if ((error != ACPU_ESUCCESS) || (up.up_climask == 0)) port_to_annex_authorize(Acp, REQ_DIALB_GRANT, CLI_MASK,Name,0); else port_to_annex_authorize(Acp, REQ_DIALB_GRANT, up.up_climask,Name,0); if (debug) { printf("check_dialback: calling acp_request_dialout\n"); fflush(stdout); } /* log this dialbakc request . */ Message[0] = 0; (void) strcat ( Message, (char *)Name ) ; (void) strcat ( Message, ",dialback request"); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_LOGIN, Message); if (debug) { printf("check_dialback log: %s.\n",Message); fflush(stdout); } errno = -1; /* * give the requesting annex time to disconnect * and release the port. This is necessary in case * both the dial in and the dial out go through * the same annex port: if we don't sleep the * dialout process will find the port is busy * disconnecting. Wait a few seconds. */ (void)sleep(DIALBACK_DELAY * ONE_SECOND); /* try the pools for dialback. */ do { int errno2; /* get Annex addr.and port set from pool member */ (void) bcopy(pool_info.pe_ports[dialb_ptype],Port_mask, LEN_PORT_MASK); target_inet = pool_info.pe_hostaddr; /* send request to appropriate annex */ errno = acp_request_dialout_tcp(target_inet, Name, Access_code, Phone, Job, Port_mask, &dialb_port,&dialb_ptype, service, 0); if(debug) { printf("acp_request_dialout_tcp returned errno=%d\n", errno); fflush(stdout); } /* If we were unable to connect via TCP, then the annex may be old (R10.0) and only do erpcd over UDP, so fallback and try UDP */ if (errno == DIAL_REJ) { errno = acp_request_dialout_udp(target_inet, Name, Access_code, Phone, Job, Port_mask, &dialb_port, &dialb_ptype, service, 0); if(debug) { printf("acp_request_dialout_udp returned errno=%d\n", errno); fflush(stdout); } } if (errno == DIAL_SUCC) break; /* dialback request failed: try next pool member */ errno2 = get_next_pool_entry(&pool_info); if (errno2 == ACPU_ESUCCESS) errno = errno2; if (debug) { printf("get_next_pool_entry ret %d\n",errno2); fflush(stdout); } } while (errno == ACPU_ESUCCESS); /* log dialback success */ if(ISUDP(Acp->state)) dialback_log(inet, port,ptype, Name, Phone, target_inet, logid, dialb_port,dialb_ptype, service, Message); if (debug) { printf("check_dialback log: %s.\n",Message); fflush(stdout); } } } /* else,for if(!passed) */ finished: /* release the environment */ release_env(env_p); release_uprof(&up); return; } /* * ipx_check_dialback() is called by ipx_validate() on the host to * determine if this is a valid dialback request, in which case * the user is prompted for the access code and (if all goes * well) the dialback request is started. * * This is what we are trying to do: * * * go through the username and password authentication * if (this Annex port number not included in any pool) * { * this is a direct connect * } * else * { * prompt for Access_Code * if (bad access code) * goto reject_request * else if (charge back with out a phone number) * goto reject_request * else if (port not member if inbound pool) * goto reject_request * else if (outbound pool name not specified) * this is a direct connect * else if (telephone number not specified) * prompt user for telephone number * if (telephone number not given) * goto reject_request * else * dial back * } * */ void ipx_check_dialback(Acp, logid, inet, port,ptype, service, Name, Netnum, ipx_phone) ACP *Acp; /* Handle to pass to library functions */ UINT32 logid, /* Log sequence number */ inet; /* Annex Internet address */ int port,ptype, /* physical/virtual port number */ service; /* Expect SERVICE_CLI */ ACP_USTRING Name; int Netnum; /* IPX Network Number */ char *ipx_phone; /* Phone number to match on */ { char Access_code[ACP_MAXSTRING], Phone[LEN_PHONE]; char Job[LEN_JOB], Port_mask[LEN_PORT_MASK]; char In_pool[ACP_LONGSTRING], Out_pool[ACP_LONGSTRING]; char Message[TOKEN_SIZE]; int passed; int got_str; UINT32 target_inet, ret_err_code = REJECT_CODE(CODE_UNKNOWN, REJ_ERPCDDENY); int dialb_port=port; int dialb_ptype = ptype; PoolEntry pool_info; int error; /* returned values from userinfo search */ Uprof up; /* storage for userinfo entries. */ struct environment_spec *env_p; /* storage for user's environment */ struct env_gr_info envinfo;/* user's environment and group filenames. */ Message[0] = 0; /* empty string */ /* Initialize the various uprof structs */ (void) bzero((char*)&up, sizeof(Uprof)); /* * userinfo database doesn't exist due to a corrupt acp_userinfo file, * this is a security breach and all users must be denied access and event * logged. */ if(deny_all_users) { syslog(LOG_ERR, "userinfo database is corrupted, denying access to\ all users. Check acp_userinfo file!\n"); return_serial_validate(Acp, REQ_DENIED); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, Message); return; } /* * Create user's environment, get a userinfo entry based on the * access code, check the dial-back pool and commence dial-back. * If any one of them fail, reject the request. */ if((env_p = create_env())==NULL) { /* deny request. */ return_serial_validate(Acp, REQ_DENIED); syslog(LOG_ERR,"Failed to allocate memory"); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, Message); return; } env_p->annex = inet; /* ip addr. of annex requesting authentication*/ env_p->port = port; /* port no. where request is generated from */ env_p->ptype = ptype; env_p->protocol = service; /* cli, slip, ppp ?? */ env_p->regime = (struct security_regime *)NULL; /* what regime used for authe.*/ env_p->group_list = (struct group_entry *)NULL; /* groups that user belogns to*/ /* Get the timestamp for this action */ if (get_time_stamp(&(env_p->time)) == FALSE) { /* Log this event */ syslog(LOG_ERR,"Failed to retrieve system time"); } strcpy(env_p->username, (char *)Name); /* use a keyword for the accesscode search in userinfo database */ strcpy(Access_code, IPX_ACCESS_CODE_TOK); bzero(Phone, LEN_PHONE); /* save the user's environment and group file info */ envinfo.gr_info = &fileinfo; envinfo.env = &env_p; /* * Look in the userinfo database and find an entry with username * "Name" and provided accesscode "Access_code". If such an * entry is found, get the other info. phonenumbers, job, * and pools (in/out) */ passed = access_code_validate(Name, Access_code, Phone, Job, In_pool, Out_pool, ipx_phone, &envinfo); /* If failed to find an entry, log and deny service */ if ( ! passed ) { /* send a permission denied packet */ return_serial_validate(Acp, REQ_DENIED); (void) strcat ( Message, (char *)Name ) ; (void) strcat ( Message, ",bad access code" ); /* log this event */ if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, Message); if (debug) { printf("ipx_check_dialback log: %s.\n",Message); fflush(stdout); } } /* if (!passed) */ else { /* check if we were trying to do chargeback without a phone no. */ if (!strcmp(Phone,"unknown")) { (void)return_serial_validate(Acp, REQ_GRANTED); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_LOGIN, Name); if (debug) { printf("ipx_check_dialback log: %s.\n",Name); fflush(stdout); } goto finished; }/* if (!strcmp(Phone,"unknown")) */ /* check if port belongs to inbound pool */ if (check_port_pool(inet, port,ptype, In_pool) != DIAL_SUCC) { /* request came from non authorized port: reject it */ return_serial_validate(Acp, REQ_DENIED); (void) strcat ( Message, (char *)Name ) ; (void) strcat ( Message, ",bad inbound port"); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, Message); if (debug) { printf("ipx_check_dialback log: %s.\n",Message); fflush(stdout); } goto finished; } /* if (check_port_pool(inet, port,ptype, In_pool) != DIAL_SUCC) */ else if (strlen(Out_pool) == 0) { /* * This call looks up userinfo database and chooses * the first entry that it finds and returns a pointer to the * entry. If the search fails, ie nothing for this user is found, * pointer points to an emtpy Uprof struct. */ error = get_user_profile_entry(&up, Name, &env_p, &fileinfo); /* deny found in userinfo entry */ if(up.up_deny) { /* set appropriate error message and reject request. */ ret_err_code = REJECT_CODE(CODE_UNKNOWN,REJ_DENYUSER); return_serial_validate(Acp, ret_err_code); /* log the reject */ if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, ret_err_code, Message); release_env(env_p); release_uprof(&up); return; } /* send the validation with appropriate climasks */ if ((error != ACPU_ESUCCESS) || (up.up_climask == 0)) (void)return_serial_validate(Acp, REQ_GRANTED); /* TODO: this else is missing cli_masks */ else (void)return_serial_validate(Acp, REQ_GRANTED); /* log the log-in */ if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_LOGIN, Name); if (debug) { printf("ipx_check_dialback log: %s.\n",Name); fflush(stdout); } goto finished; }/* else if (strlen(Out_pool) == 0) */ else if (get_port_pool(Out_pool, &pool_info) != ACPU_ESUCCESS) { /* Unable to read pool information: probably an invalid */ /* pool name, or data base problem: reject request */ return_serial_validate(Acp, REQ_DENIED); (void) strcat ( Message, (char *)Name ) ; (void) strcat ( Message, ",bad outbound poolname"); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, Message); if (debug) { printf("ipx_check_dialback log: %s.\n",Message); fflush(stdout); } goto finished; }/* else if (get_port_pool(Out_pool, &pool_info) != ACPU_ESUCCESS) */ else if (strlen(Phone) == 0) { /* prompt user for telephone number */ got_str = promptstring(Acp, Phone, ACP_PHONEPROMPT, 1, INPUT_TIMEOUT); if (debug) { printf("ipx_check_dialback: got_str=%d, phone=%s.\n", got_str,Phone); fflush(stdout); } }/* else if (strlen(Phone) == 0) */ if (strlen(Phone) == 0) { /* error: don't have the phone number */ return_serial_validate(Acp, REQ_DENIED); (void) strcat ( Message, (char *)Name ) ; (void) strcat ( Message, ",no phone number"); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_REJECT, Message); if (debug) { printf("ipx_check_dialback log: %s.\n",Message); fflush(stdout); } }/* if (strlen(Phone) == 0) */ else { /* * This call looks up userinfo database and chooses * the first entry that it finds and returns a pointer to the * entry. If the search fails, ie nothing for this user is found, * pointer points to an emtpy Uprof struct. */ errno = get_user_profile_entry(&up, Name, &env_p, &fileinfo); /* deny found in userinfo entry */ if(up.up_deny) { /* set appropriate error code */ ret_err_code = REJECT_CODE(CODE_UNKNOWN,REJ_DENYUSER); /* deny user and log */ return_serial_validate(Acp, REQ_DENIED); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, ret_err_code, Message); release_env(env_p); release_uprof(&up); return; } /* grant dialback */ (void)return_serial_validate(Acp, REQ_DIALB_GRANT); if (debug) { printf("ipx_check_dialback: calling acp_request_dialout\n"); fflush(stdout); } Message[0] = 0; (void) strcat ( Message, (char *)Name ) ; (void) strcat ( Message, ",dialback request"); if(ISUDP(Acp->state)) log_message(inet, logid, port,ptype, service, EVENT_LOGIN, Message); if (debug) { printf("ipx_check_dialback log: %s.\n",Message); fflush(stdout); } errno = -1; /* * give the requesting annex time to disconnect * and release the port. This is necessary in case * both the dial in and the dial out go through * the same annex port: if we don't sleep the * dialout process will find the port is busy * disconnecting. Wait a few seconds. */ (void)sleep(6 * ONE_SECOND); do /* try the pools for dialback. */ { int errno2; /* get Annex addr.and port set from pool member */ (void) bcopy(pool_info.pe_ports[dialb_ptype], Port_mask, LEN_PORT_MASK); target_inet = pool_info.pe_hostaddr; /* send request to appropriate annex */ errno = acp_request_dialout_tcp(target_inet, Name, Access_code, Phone, Job, Port_mask, &dialb_port,&dialb_ptype, service, Netnum); if(debug) { printf("acp_request_dialout_tcp returned errno=%d\n",errno); fflush(stdout); } /* If we were unable to connect via TCP, then the annex may be old (R10.0) and only do erpcd over UDP, so fallback and try UDP */ if (errno == DIAL_REJ) { errno = acp_request_dialout_udp(target_inet, Name, Access_code, Phone, Job, Port_mask, &dialb_port, &dialb_ptype, service, Netnum); if(debug) { printf("acp_request_dialout_udp returned errno=%d\n", errno); fflush(stdout); } } if (errno == DIAL_SUCC) break; /* dialback request failed: try next pool member */ errno2 = get_next_pool_entry(&pool_info); if (errno2 == ACPU_ESUCCESS) errno = errno2; if (debug) { printf("get_next_pool_entry ret %d\n",errno2); fflush(stdout); } } while (errno == ACPU_ESUCCESS); if (ISUDP(Acp->state)) dialback_log(inet, port,ptype, Name, Phone, target_inet, logid, dialb_port,dialb_ptype, service, Message); if (debug) { printf("ipx_check_dialback log: %s.\n",Message); fflush(stdout); } }/*else, for if (strlen(Phone) == 0) */ }/*else ,for if(!passed ) */ finished: /* free the alloc'ed memory */ release_env(env_p); release_uprof(&up); return; } void dialback_log(inet, port,ptype, Name, Phone, target_inet, logid, dialb_port,dialb_ptype, service, Message) UINT32 inet; char *Name, *Phone; UINT32 target_inet; UINT32 logid; int dialb_port,service; char *Message; int port,ptype,dialb_ptype; { /* do message logging */ Message[0] = 0; switch(errno) { case DIAL_SUCC: (void) sprintf(Message,"%s, tel:%s", Name, Phone); log_message(target_inet, logid, dialb_port,dialb_ptype, service, EVENT_DIAL, Message); break; case DIAL_EBUSY: (void) sprintf(Message,"%s, ports busy", Name); log_message(inet, logid, port,ptype, service, EVENT_DIAL, Message); break; case DIAL_ETIMEDOUT: (void) sprintf(Message,"%s, timed out", Name); log_message(inet, logid, dialb_port,dialb_ptype, service, EVENT_DIAL, Message); break; case DIAL_TIME: (void) sprintf(Message,"%s, srpc timed out", Name); log_message(inet, logid, port,ptype, service, EVENT_DIAL, Message); break; case DIAL_EINVAL: (void) sprintf(Message,"%s, unknown modem", Name); log_message(inet, logid, dialb_port,dialb_ptype, service, EVENT_DIAL, Message); break; case DIAL_EIO: (void) sprintf(Message,"%s, I/O error", Name); log_message(inet, logid, dialb_port,dialb_ptype, service, EVENT_DIAL, Message); break; default: (void) sprintf(Message,"%s, failed", Name); log_message(inet, logid, port,ptype, service, EVENT_DIAL, Message); break; } return; } #ifdef USE_NDBM /* Waits to use the database and then breaks the lock on it after a period of 5 seconds*/ int erpcd_lock_acp_dbm() { struct stat *stats; char str[ACP_LONGSTRING]; int i, rv=0; u_short set_lock_flag = FALSE; if((stats = (struct stat *)malloc(sizeof(struct stat))) == NULL) { errno = ENOMEM; process_error(NULL, -1); return -1; } /* check the permissions& modes for acp_dbm.db and acp_dbm.pag. */ ACP_DBM_DIR(str); if (stat(str, stats) == -1) { if (errno != ENOENT) { syslog(LOG_CRIT, "Error reading from acp_dbm\n"); free(stats); return -1; } } else if(stats->st_mode != ROOT_RDWR) { sprintf(syslogbuf, "Wrong permissions on file: %s\n", str); syslog(LOG_CRIT, syslogbuf); free(stats); erpcd_dbmErrorTrap (ERPCD_TRAP_PROTECT); return -1; } ACP_DBM_PAGE(str); if (stat(str, stats) == -1) { if (errno != ENOENT) { syslog(LOG_CRIT, "Error reading from acp_dbm\n"); free(stats); return -1; } } else if(stats->st_mode != ROOT_RDWR) { sprintf(syslogbuf, "Wrong permissions on file: %s\n", str); syslog(LOG_CRIT, syslogbuf); free(stats); erpcd_dbmErrorTrap (ERPCD_TRAP_PROTECT); return -1; } free(stats); /* acp_dbm exists, try locking */ for( i=0; i<5; i++) { rv = dbm_lock_acp_dbm(); if(rv == -2) { rv = -1; syslog(LOG_CRIT, "Error in locking acp_dbm\n"); break; } if(rv == -1) sleep(1); else { set_lock_flag = TRUE; break; } } if((set_lock_flag==FALSE) && (dbm_lock_acp_dbm() == -1)) { dbm_unlock_acp_dbm(); rv = dbm_lock_acp_dbm(); if(rv == -2) { rv=-1; syslog(LOG_CRIT, "Error in locking acp_dbm\n"); } } return rv; } /* Matches users entered password with the previous history of passwords*/ int matches_old_password(user, new) char *user; char *new; { int erpcd_lock_acp_dbm(); DBM *dbm; int rv, i; char *pw, old_password[MAX_STORED_PASS][HASHLEN + 1], str[ACP_LONGSTRING]; if(erpcd_lock_acp_dbm()==-1) return -1; sprintf(str, "%s/", install_dir); strcat(str, ACP_DBM_FILE); dbm = dbm_open(str, (O_CREAT | O_RDWR), 0600); if(dbm != NULL) { bzero(old_password, MAX_STORED_PASS * (HASHLEN + 1)); rv=dbm_get_old_pwds(dbm, user, old_password); unlock_database(dbm); if(rv == 0) { for(i=0; ienv; CHAP_REQ *chap = Acp->chap; SECPORT secport; secport.unit = env_p->port; secport.type = env_p->ptype; #define MULTIPLE_REGIMES #undef MULTIPLE_REGIMES Message[0] = '\0'; /* Get the timestamp for this action */ if (get_time_stamp(&(env_p->time)) == FALSE) { /* Log this event */ syslog(LOG_ERR, "Failed to retrieve the system time"); } /* prompt for Name and Password until valid or too many tries */ /* * Get the user's name. Call get_security_regime() which * reads acp_regime file and gets regime and password info. * for user's authentication. */ for (tries = 0; tries < max_retries && done == FALSE; tries++) { regimes_passed = 0; regimes_tried = 0; /* Prompt for the users name */ if(prompt) got_str = promptstring(Acp, Name, userprompt, 1, INPUT_TIMEOUT); /* if username provided. */ if (got_str > 0) { /* save the username */ strncpy(env_p->username, Name, LEN_USERNAME -1); env_p->username[LEN_USERNAME -1] = '\0'; env_p->regime = (struct security_regime *)NULL; #if (USER_VALIDATION == 1) /* * for ipx_validate and ppp_security, fall through. * If (USER_VALIDATE !=1), just fall through. We only * need the user's environment which can be used * by the calling fxn. to get the cli commands (if * specified) for this user. */ if ((Pass[0] == '\0') && !prompt) passed = VALIDATED; #ifdef _WIN32 else if ( chap && !ErpcdOpt->RadiusAuthentication ) { /* this section does chap via acp_userinfo */ /* radius is done in acp_radius_validate() */ int valid = 0; ACP_STRING secret; Acp->auth.ret_err_code = get_chap_secret(env_p, secret); switch(Acp->auth.ret_err_code) { case EVENT_PROVIDE: valid = verify_chap(chap, secret); valid = record_login(valid, &Acp->auth.blacklisted, Name); if (valid) passed = VALIDATED; else passed = NOT_VALIDATED; break; case EVENT_NOPROVIDE: default: passed = NOT_VALIDATED; break; } } else { /* (note: got_str is >0 here) */ if(prompt && !ErpcdOpt->SecuridAuthentication && !ErpcdOpt->SafewordAuthentication) got_str = promptstring(Acp, Pass, ACP_PASSPROMPT, 0, INPUT_TIMEOUT); if (got_str > 0) { if (debug) { printf("Using RadiusAuthentication: %s\n", ErpcdOpt->RadiusAuthentication ? "TRUE" : "FALSE"); if(ErpcdOpt->SecuridAuthentication) printf("Using SecurID Authentication\n"); if(ErpcdOpt->SafewordAuthentication) { #ifndef BAY_ALPHA SwecVersionRec swecVersionInfo; swecVersion(&swecVersionInfo); printf("Using SafeWord Authentication: %s\n", swecVersionInfo.description ? swecVersionInfo.description : ""); #endif } } if ( ErpcdOpt->RadiusAuthentication ) { passed = acp_radius_validate(Acp, prompt, Name, Pass, &secport, opt_info); } else if ( ErpcdOpt->SecuridAuthentication ) { passed = acp_securid_authenticate(Acp, logid, Acp->inet, secport.unit, secport.type, Acp->env->protocol, Name, tries, NULL); } else if ( ErpcdOpt->SafewordAuthentication ) { #ifdef ENIGMA_SAFEWORD passed = acp_safeword_validate(Acp, logid, Acp->inet, secport.unit, secport.type, Acp->env->protocol, &got_str, Name); if (passed == VALIDATED) outputstring(Acp, EAS_PERMGRANTD); else outputstring(Acp, EAS_INCORRECT); if (got_str <= 0 || passed <= 0) passed = NOT_VALIDATED; else passed = VALIDATED; #else /* not ENIGMA_SAFEWORD */ passed = NOT_VALIDATED; #endif /* not ENIGMA_SAFEWORD */ } else { passed = NTValidate(Name, Pass); } done = passed; if(!passed) { if (strlen(Message)) { strcat(Message, ","); } strcat(Message, Name); if(prompt && tries < (max_retries -1)) outputstring(Acp, ACP_INCORRECT); else if (prompt) outputstring(Acp, ACP_PERMDENIED); } } else if (!got_str && prompt){ Acp->auth.ret_err_code = EVENT_TIMEOUT; outputstring(Acp, ACP_TIMEDOUT); } } #else /* not _WIN32 */ /* * get the user's regime && password/ticket-dir * for authentication */ else if (get_security_regime(env_p)) { /* use the acquired regime. */ regime = env_p->regime; if (debug) fprintf(stderr, "acp_policy.c: the regime_mask is %d \n", regime->regime_mask); /* * use the regime specified in the user's * environment. */ if (chap && regime->regime_mask != RADIUS_MASK) { /* this section does chap via acp_userinfo */ /* radius is done in acp_radius_validate() */ int valid = 0; ACP_STRING secret; Acp->auth.ret_err_code = get_chap_secret(env_p, secret); switch(Acp->auth.ret_err_code) { case EVENT_PROVIDE: valid = verify_chap(chap, secret); valid = record_login(valid, &Acp->auth.blacklisted, Name); if (valid) passed = VALIDATED; else passed = NOT_VALIDATED; break; case EVENT_NOPROVIDE: default: passed = NOT_VALIDATED; break; } } else { switch(regime->regime_mask) { case KERBEROS_MASK: case NATIVE_MASK: case ACP_MASK: case RADIUS_MASK: passed = NOT_VALIDATED; if (prompt) { if ((got_str = promptstring(Acp, Pass, ACP_PASSPROMPT, 0, INPUT_TIMEOUT)) <= 0) if(!got_str && prompt){ Acp->auth.ret_err_code = EVENT_TIMEOUT; outputstring(Acp, ACP_TIMEDOUT); } } switch(regime->regime_mask) { case KERBEROS_MASK: passed = acp_kerberos_validate(Name, Pass); break; case NATIVE_MASK: passed = acp_native_validate(Name, Pass); break; case ACP_MASK: passed = acp_validate(Name,Pass,&Acp->auth.blacklisted, regime->regime_supplement.password_file); break; case RADIUS_MASK: passed = acp_radius_validate(Acp, prompt, Name, Pass, &secport, opt_info); break; } /* switch */ if (prompt) { if (passed > 0){ #ifdef USESHADOW warn_user(Acp, &passed, Name); #endif } else if (tries < (max_retries - 1)) outputstring(Acp, ACP_INCORRECT); else outputstring(Acp, ACP_PERMDENIED); } if (passed > 0) passed = VALIDATED; break; /* for securid */ case SECURID_MASK: if (prompt == FALSE) { passed = acp_securid_authenticate(Acp, logid, Acp->inet, secport.unit, secport.type, Acp->env->protocol, Name, tries, Pass); } else { passed = acp_securid_authenticate(Acp, logid, Acp->inet, secport.unit, secport.type, Acp->env->protocol, Name, tries, NULL); #ifdef PASS_SEC if (passed > 0){ if ((got_str = promptstring(Acp, Pass, ACP_PASSPROMPT, 0, INPUT_TIMEOUT)) <= 0) if(!got_str){ Acp->auth.ret_err_code = EVENT_TIMEOUT; outputstring(Acp, ACP_TIMEDOUT); passed = NOT_VALIDATED; done = TRUE; break; } #ifdef NATIVEPASSWD passed = acp_native_validate(Name, Pass); #else passed = acp_validate(Name,Pass,&Acp->auth.blacklisted, NULL); #endif if (passed > 0){ #ifdef USESHADOW warn_user(Acp, &passed, Name); #endif } } #endif } if(passed == USER_ABORT) done=TRUE; break; /* SECURID_MASK */ /* for enigma-safeword */ case SAFEWORD_MASK: #ifdef ENIGMA_SAFEWORD passed = acp_safeword_validate(Acp, logid, Acp->inet, secport.unit, secport.type, Acp->env->protocol, &got_str, Name); if (passed == VALIDATED) outputstring(Acp, EAS_PERMGRANTD); else outputstring(Acp, EAS_INCORRECT); if (got_str <= 0 || passed <= 0) passed = NOT_VALIDATED; else passed = VALIDATED; #else passed = NOT_VALIDATED; #endif break; /* SAFEWORD_MASK */ /* for deny, undocumented regime */ case DENY_MASK: passed = NOT_VALIDATED; break; case NONE_MASK: passed = VALIDATED; done = TRUE; break; case NO_REGIME_MASK: default: passed = NOT_VALIDATED; break; }/* End regime switch */ } /* if chap... */ if (passed != VALIDATED) { /* Log the failed user */ regime_used = map_mask_to_code(regime->regime_mask); if(passed == UNAVAILABLE) Acp->auth.ret_err_code = REJECT_CODE(regime_used, REJ_REGNOTAVAIL); else Acp->auth.ret_err_code = REJECT_CODE(regime_used, REJ_REGDENY); if (strlen(Message)) strcat(Message, ","); strcat(Message, Name); } /* else ,for, if(passed == VALIDATED) */ /* Now have we passed all the specified regimes */ if (passed == VALIDATED) { done = TRUE; } } /* else if (get_security_regime(env_p)) */ /* * No security regime found. denying * acces. */ else { /* Log this event */ Acp->auth.ret_err_code = REJECT_CODE(CODE_UNKNOWN, REJ_ERPCDDENY); if (strlen(Message)) strcat(Message, ","); strcat(Message, Name); /* adding fake prompting here */ if(prompt) got_str = promptstring(Acp, Pass, ACP_PASSPROMPT, 0, INPUT_TIMEOUT); /* * put the thread to sleep for 2 seconds * to avoid a rapid-fire condition. */ sleep(2); }/* If there is a regime */ #endif /* not _WIN32 [from before if (get_security_regime...)] */ #else /* (USER_VALIDATION) */ /* * just fall through, we got what we need * - user's environment. */ done = TRUE; passed = VALIDATED; #endif /* (USER_VALIDATION) */ }/* if got_str gets a username */ else { done = TRUE; if(!got_str && prompt){ Acp->auth.ret_err_code = EVENT_TIMEOUT; outputstring(Acp, ACP_TIMEDOUT); } } }/* End retries - for loop for regimes. */ #ifndef _WIN32 #ifdef PORT_PASSWORD if ((secport.type == DEV_SERIAL || secport.type == DEV_V120) && (passed > NOT_VALIDATED) && prompt) passed = acp_port_password_authenticate(Acp, logid, Acp->inet, secport.unit, secport.type, Acp->env->protocol, max_retries); #endif #endif return passed; } /* * Map regime masks to regime codes. * Returns: * regime code. */ static int map_mask_to_code(regime_mask) int regime_mask; { switch(regime_mask) { case ACP_MASK: regime_mask = CODE_ACP ; break; case SAFEWORD_MASK: regime_mask = CODE_SAFEWORD; break; case KERBEROS_MASK: regime_mask = CODE_KERBEROS; break; case NATIVE_MASK: regime_mask = CODE_NATIVE; break; case SECURID_MASK: regime_mask = CODE_SECURID; break; case DENY_MASK: regime_mask = CODE_DENY; break; case NONE_MASK: regime_mask = CODE_NONE; break; default: regime_mask = CODE_UNKNOWN; break; } return(regime_mask); } /***************************************************************************** * * NAME: UINT32 get_chap_secret() * * DESCRIPTION: Retrieves the chap secret from the userinfo database * * ARGUMENTS: * struct environment_spec *env - INPUT Security Profile for this request * char *secret - OUTPUT Retrieved chap secret * * RETURN VALUE: * EVENT_PROVIDE - an entry was found * REJ_NOUSERINFO - user was not found * REJ_NOPWDINFO - chap secret not found * other - error code indicating why entry was not retrieved * * RESOURCE HANDLING: * * SIDE EFFECTS: * * EXCEPTIONS: * * ASSUMPTIONS: * */ UINT32 get_chap_secret(env, secret) struct environment_spec *env; char *secret; { Uprof up; int error; UINT32 status; (void) bzero((char*)&up, sizeof(Uprof)); error = get_user_profile_entry(&up, env->username, &env, &fileinfo); /* If user has a deny entry in userinfo entry, deny access. */ if(up.up_deny) status = (REJECT_CODE(CODE_UNKNOWN,REJ_DENYUSER)); else if (up.up_de.de_key[0] == 0) status = (REJECT_CODE(CODE_UNKNOWN,REJ_NOUSERINFO)); else bcopy(up.up_secret,secret,sizeof(up.up_secret)); if (strlen(secret)) status = (EVENT_PROVIDE); else status = (REJECT_CODE(CODE_UNKNOWN,REJ_NOPWDINFO)); release_uprof(&up); return (status); } /***************************************************************************** * * NAME: UINT32 verify_chap() * * DESCRIPTION: Verifies the chap response from the remote peer * * ARGUMENTS: * CHAP_REQ *chap - INPUT chap info * char *secret - INPUT chap secret * * RETURN VALUE: TRUE - response is good * * RESOURCE HANDLING: * * SIDE EFFECTS: * * EXCEPTIONS: * * ASSUMPTIONS: * */ int verify_chap(chap, secret) CHAP_REQ *chap; char *secret; { u_char cat[MAX_MD5]; u_char result[CHAP_RESP_LEN]; int valid; bzero((char*)cat, MAX_MD5); cat[0] = chap->id; strcpy((char *)cat+1, secret); bcopy((char*)chap->challenge, (char*)&cat[1 + strlen(secret)], CHAP_CHAL_LEN); cat[1 + strlen(secret) + CHAP_CHAL_LEN] = '\0'; MDString(cat, (1 + strlen(secret) + CHAP_CHAL_LEN), result); valid = (memcmp(chap->response, result, CHAP_RESP_LEN) == 0); return(valid); } /***************************************************************************** * * NAME: void chap_authenticate() * * DESCRIPTION: Authenticates a chap session (TCP only) * * ARGUMENTS: * ACP *acp - INPUT acp for this connection * SECPORT *port - INPUT port over which the ppp connection is authenticated * char *user - INPUT username being authenticated * * RETURN VALUE: None * Calls acp_auth_resp() to send back authentication status to RA * * RESOURCE HANDLING: * * SIDE EFFECTS: * * EXCEPTIONS: * * ASSUMPTIONS: * */ void chap_authenticate(acp, port, user, opt_info) ACP *acp; SECPORT *port; char *user; ARQ_PROFILE *opt_info; { int valid = 0; struct environment_spec env; char String[TOKEN_SIZE]; /* Any token of size 256 or less */ ACP_STRING pwd; acp->auth.blacklisted = FALSE; acp->auth.ret_err_code = EVENT_PROVIDE; if(deny_all_users) { syslog(LOG_ERR, "chap_authenticate: userinfo database is corrupted, denying access to all users. Check acp_userinfo file!\n"); acp_auth_resp(acp, REQ_DENIED, NULL, NULL, NULL); terminate_session(); return; } acp->env = &env; bzero((char*)acp->env, sizeof(struct environment_spec)); strncpy(env.username, user, MAX_OPTION - 1); user[MAX_OPTION - 1] = '\0'; env.annex = acp->inet; env.port = port->unit; env.ptype = port->type; env.protocol = SERVICE_PPP; acp->auth.blacklisted = FALSE; strcpy(pwd, "kludge"); valid = generic_authenticate_user(acp, 0, user, pwd, FALSE, 1, String, opt_info); switch(acp->auth.ret_err_code) { case EVENT_PROVIDE: if (valid) acp_auth_resp(acp, REQ_GRANTED, NULL, NULL, NULL); else acp_auth_resp(acp, REQ_DENIED, NULL, NULL, NULL); if (acp->auth.blacklisted) write_audit_log(acp->inet, 0, port, SERVICE_PPP, EVENT_BLACKLIST, 0, NULL, NULL, user, NULL); break; case EVENT_NOPROVIDE: default: acp_auth_resp(acp, acp->auth.ret_err_code, NULL, NULL, NULL); break; } terminate_session(); } /***************************************************************************** * * NAME: int record_login() * * DESCRIPTION: Records a login attempt for blacklisting * * ARGUMENTS: * int success - INPUT TRUE if login attempt was successful * int *blacklist - OUTPUT points to a TRUE value if the user is blacklisted * char *user - INPUT username used for login attempt * * RETURN VALUE: TRUE if the user should be accepted, FALSE denied * * RESOURCE HANDLING: * * SIDE EFFECTS: * * EXCEPTIONS: * * ASSUMPTIONS: * */ int record_login(success, blacklist, user) int success; int *blacklist; char *user; { #ifdef USE_NDBM int rv = 0; DBM *dbm; char str[PATHSZ]; /* for pathnames/filenames. */ rv = erpcd_lock_acp_dbm(); if(rv == 0){ sprintf(str, "%s/", install_dir); strcat(str, ACP_DBM_FILE); dbm = dbm_open(str, (O_CREAT | O_RDWR), 0600); if(dbm != NULL){ if(success){ rv = dbm_verify_login_success(dbm, user); if((rv != TRUE && errno != ENOENT) || rv == FALSE) success = FALSE; if(rv < 0 && errno != ENOENT) print_error(user, rv); } else{ if( maxcon != -1 || maxtotal != -1){ rv = dbm_record_login_failure(dbm, user, maxcon, maxtotal, period); } if(rv < 0 ) print_error(user, rv); else if (blacklist != NULL && rv == 1) *blacklist = TRUE; } unlock_database(dbm); } else{ syslog(LOG_CRIT, "%m"); success = FALSE; dbm_unlock_acp_dbm(); } } else { success = FALSE; dbm_unlock_acp_dbm(); } #endif return(success); }