diff options
Diffstat (limited to 'pam_radius_auth.c')
-rw-r--r-- | pam_radius_auth.c | 2605 |
1 files changed, 1283 insertions, 1322 deletions
diff --git a/pam_radius_auth.c b/pam_radius_auth.c index 008dc69..39dc755 100644 --- a/pam_radius_auth.c +++ b/pam_radius_auth.c @@ -81,82 +81,82 @@ static time_t session_time; /* logging */ static void _pam_log(int err, CONST char *format, ...) { - va_list args; - char buffer[BUFFER_SIZE]; - - va_start(args, format); - vsprintf(buffer, format, args); - /* don't do openlog or closelog, but put our name in to be friendly */ - syslog(err, "%s: %s", pam_module_name, buffer); - va_end(args); + va_list args; + char buffer[BUFFER_SIZE]; + + va_start(args, format); + vsprintf(buffer, format, args); + /* don't do openlog or closelog, but put our name in to be friendly */ + syslog(err, "%s: %s", pam_module_name, buffer); + va_end(args); } /* argument parsing */ static int _pam_parse(int argc, CONST char **argv, radius_conf_t *conf) { - int ctrl=0; - - memset(conf, 0, sizeof(radius_conf_t)); /* ensure it's initialized */ - - strcpy(conf_file, CONF_FILE); - - /* - * If either is not there, then we can't parse anything. - */ - if ((argc == 0) || (argv == NULL)) { - return ctrl; - } - - /* step through arguments */ - for (ctrl=0; argc-- > 0; ++argv) { - - /* generic options */ - if (!strncmp(*argv,"conf=",5)) { - strcpy(conf_file,*argv+5); - - } else if (!strcmp(*argv, "use_first_pass")) { - ctrl |= PAM_USE_FIRST_PASS; - - } else if (!strcmp(*argv, "try_first_pass")) { - ctrl |= PAM_TRY_FIRST_PASS; - - } else if (!strcmp(*argv, "skip_passwd")) { - ctrl |= PAM_SKIP_PASSWD; - - } else if (!strncmp(*argv, "retry=", 6)) { - conf->retries = atoi(*argv+6); - - } else if (!strcmp(*argv, "localifdown")) { - conf->localifdown = 1; - - } else if (!strncmp(*argv, "client_id=", 10)) { - if (conf->client_id) { - _pam_log(LOG_WARNING, "ignoring duplicate '%s'", *argv); - } else { - conf->client_id = (char *) *argv+10; /* point to the client-id */ - } - } else if (!strcmp(*argv, "accounting_bug")) { - conf->accounting_bug = TRUE; - - } else if (!strcmp(*argv, "ruser")) { - ctrl |= PAM_RUSER_ARG; - - } else if (!strcmp(*argv, "debug")) { - ctrl |= PAM_DEBUG_ARG; - conf->debug = 1; - - } else { - _pam_log(LOG_WARNING, "unrecognized option '%s'", *argv); - } - } - - return ctrl; + int ctrl=0; + + memset(conf, 0, sizeof(radius_conf_t)); /* ensure it's initialized */ + + strcpy(conf_file, CONF_FILE); + + /* + * If either is not there, then we can't parse anything. + */ + if ((argc == 0) || (argv == NULL)) { + return ctrl; + } + + /* step through arguments */ + for (ctrl=0; argc-- > 0; ++argv) { + + /* generic options */ + if (!strncmp(*argv,"conf=",5)) { + strcpy(conf_file,*argv+5); + + } else if (!strcmp(*argv, "use_first_pass")) { + ctrl |= PAM_USE_FIRST_PASS; + + } else if (!strcmp(*argv, "try_first_pass")) { + ctrl |= PAM_TRY_FIRST_PASS; + + } else if (!strcmp(*argv, "skip_passwd")) { + ctrl |= PAM_SKIP_PASSWD; + + } else if (!strncmp(*argv, "retry=", 6)) { + conf->retries = atoi(*argv+6); + + } else if (!strcmp(*argv, "localifdown")) { + conf->localifdown = 1; + + } else if (!strncmp(*argv, "client_id=", 10)) { + if (conf->client_id) { + _pam_log(LOG_WARNING, "ignoring duplicate '%s'", *argv); + } else { + conf->client_id = (char *) *argv+10; /* point to the client-id */ + } + } else if (!strcmp(*argv, "accounting_bug")) { + conf->accounting_bug = TRUE; + + } else if (!strcmp(*argv, "ruser")) { + ctrl |= PAM_RUSER_ARG; + + } else if (!strcmp(*argv, "debug")) { + ctrl |= PAM_DEBUG_ARG; + conf->debug = 1; + + } else { + _pam_log(LOG_WARNING, "unrecognized option '%s'", *argv); + } + } + + return ctrl; } /* Callback function used to free the saved return value for pam_setcred. */ -void _int_free( pam_handle_t * pamh, void *x, int error_status ) +void _int_free(pam_handle_t * pamh, void *x, int error_status) { - free(x); + free(x); } /************************************************************************* @@ -168,72 +168,72 @@ void _int_free( pam_handle_t * pamh, void *x, int error_status ) * one supplied in standard dot notation. */ static UINT4 ipstr2long(char *ip_str) { - char buf[6]; - char *ptr; - int i; - int count; - UINT4 ipaddr; - int cur_byte; - - ipaddr = (UINT4)0; - - for(i = 0;i < 4;i++) { - ptr = buf; - count = 0; - *ptr = '\0'; - - while(*ip_str != '.' && *ip_str != '\0' && count < 4) { - if(!isdigit(*ip_str)) { - return((UINT4)0); - } - *ptr++ = *ip_str++; - count++; - } - - if(count >= 4 || count == 0) { - return((UINT4)0); - } - - *ptr = '\0'; - cur_byte = atoi(buf); - if(cur_byte < 0 || cur_byte > 255) { - return ((UINT4)0); - } - - ip_str++; - ipaddr = ipaddr << 8 | (UINT4)cur_byte; - } - return(ipaddr); + char buf[6]; + char *ptr; + int i; + int count; + UINT4 ipaddr; + int cur_byte; + + ipaddr = (UINT4)0; + + for(i = 0;i < 4;i++) { + ptr = buf; + count = 0; + *ptr = '\0'; + + while(*ip_str != '.' && *ip_str != '\0' && count < 4) { + if(!isdigit(*ip_str)) { + return((UINT4)0); + } + *ptr++ = *ip_str++; + count++; + } + + if(count >= 4 || count == 0) { + return((UINT4)0); + } + + *ptr = '\0'; + cur_byte = atoi(buf); + if(cur_byte < 0 || cur_byte > 255) { + return ((UINT4)0); + } + + ip_str++; + ipaddr = ipaddr << 8 | (UINT4)cur_byte; + } + return(ipaddr); } /* * Check for valid IP address in standard dot notation. */ static int good_ipaddr(char *addr) { - int dot_count; - int digit_count; - - dot_count = 0; - digit_count = 0; - while(*addr != '\0' && *addr != ' ') { - if(*addr == '.') { - dot_count++; - digit_count = 0; - } else if(!isdigit(*addr)) { - dot_count = 5; - } else { - digit_count++; - if(digit_count > 3) { - dot_count = 5; - } - } - addr++; - } - if(dot_count != 3) { - return(-1); - } else { - return(0); - } + int dot_count; + int digit_count; + + dot_count = 0; + digit_count = 0; + while(*addr != '\0' && *addr != ' ') { + if(*addr == '.') { + dot_count++; + digit_count = 0; + } else if(!isdigit(*addr)) { + dot_count = 5; + } else { + digit_count++; + if(digit_count > 3) { + dot_count = 5; + } + } + addr++; + } + if(dot_count != 3) { + return(-1); + } else { + return(0); + } } /* @@ -241,91 +241,88 @@ static int good_ipaddr(char *addr) { * name or address in dot notation. */ static UINT4 get_ipaddr(char *host) { - struct hostent *hp; - - if(good_ipaddr(host) == 0) { - return(ipstr2long(host)); + struct hostent *hp; - } else if((hp = gethostbyname(host)) == (struct hostent *)NULL) { - return((UINT4)0); - } + if(good_ipaddr(host) == 0) { + return(ipstr2long(host)); + } else if((hp = gethostbyname(host)) == (struct hostent *)NULL) { + return((UINT4)0); + } - return(ntohl(*(UINT4 *)hp->h_addr)); + return(ntohl(*(UINT4 *)hp->h_addr)); } /* * take server->hostname, and convert it to server->ip and server->port */ -static int -host2server(radius_server_t *server) +static int host2server(radius_server_t *server) { - char *p; - int ctrl = 1; /* for DPRINT */ - - if ((p = strchr(server->hostname, ':')) != NULL) { - *(p++) = '\0'; /* split the port off from the host name */ - } - - if ((server->ip.s_addr = get_ipaddr(server->hostname)) == ((UINT4)0)) { - DPRINT(LOG_DEBUG, "DEBUG: get_ipaddr(%s) returned 0.\n", server->hostname); - return PAM_AUTHINFO_UNAVAIL; - } - - /* - * If the server port hasn't already been defined, go get it. - */ - if (!server->port) { - if (p && isdigit(*p)) { /* the port looks like it's a number */ - unsigned int i = atoi(p) & 0xffff; - - if (!server->accounting) { - server->port = htons((u_short) i); - } else { - server->port = htons((u_short) (i + 1)); - } - } else { /* the port looks like it's a name */ - struct servent *svp; - - if (p) { /* maybe it's not "radius" */ - svp = getservbyname (p, "udp"); - /* quotes allow distinction from above, lest p be radius or radacct */ - DPRINT(LOG_DEBUG, "DEBUG: getservbyname('%s', udp) returned %d.\n", p, svp); - *(--p) = ':'; /* be sure to put the delimiter back */ - } else { - if (!server->accounting) { - svp = getservbyname ("radius", "udp"); - DPRINT(LOG_DEBUG, "DEBUG: getservbyname(radius, udp) returned %d.\n", svp); - } else { - svp = getservbyname ("radacct", "udp"); - DPRINT(LOG_DEBUG, "DEBUG: getservbyname(radacct, udp) returned %d.\n", svp); + char *p; + int ctrl = 1; /* for DPRINT */ + + if ((p = strchr(server->hostname, ':')) != NULL) { + *(p++) = '\0'; /* split the port off from the host name */ + } + + if ((server->ip.s_addr = get_ipaddr(server->hostname)) == ((UINT4)0)) { + DPRINT(LOG_DEBUG, "DEBUG: get_ipaddr(%s) returned 0.\n", server->hostname); + return PAM_AUTHINFO_UNAVAIL; } - } - - if (svp == (struct servent *) 0) { - /* debugging above... */ - return PAM_AUTHINFO_UNAVAIL; - } - - server->port = svp->s_port; - } - } - - return PAM_SUCCESS; + + /* + * If the server port hasn't already been defined, go get it. + */ + if (!server->port) { + if (p && isdigit(*p)) { /* the port looks like it's a number */ + unsigned int i = atoi(p) & 0xffff; + + if (!server->accounting) { + server->port = htons((u_short) i); + } else { + server->port = htons((u_short) (i + 1)); + } + } else { /* the port looks like it's a name */ + struct servent *svp; + + if (p) { /* maybe it's not "radius" */ + svp = getservbyname (p, "udp"); + /* quotes allow distinction from above, lest p be radius or radacct */ + DPRINT(LOG_DEBUG, "DEBUG: getservbyname('%s', udp) returned %d.\n", p, svp); + *(--p) = ':'; /* be sure to put the delimiter back */ + } else { + if (!server->accounting) { + svp = getservbyname ("radius", "udp"); + DPRINT(LOG_DEBUG, "DEBUG: getservbyname(radius, udp) returned %d.\n", svp); + } else { + svp = getservbyname ("radacct", "udp"); + DPRINT(LOG_DEBUG, "DEBUG: getservbyname(radacct, udp) returned %d.\n", svp); + } + } + + if (svp == (struct servent *) 0) { + /* debugging above... */ + return PAM_AUTHINFO_UNAVAIL; + } + + server->port = svp->s_port; + } + } + + return PAM_SUCCESS; } /* * Do XOR of two buffers. */ -static unsigned char * -xor(unsigned char *p, unsigned char *q, int length) +static unsigned char * xor(unsigned char *p, unsigned char *q, int length) { - int i; - unsigned char *retval = p; - - for (i = 0; i < length; i++) { - *(p++) ^= *(q++); - } - return retval; + int i; + unsigned char *retval = p; + + for (i = 0; i < length; i++) { + *(p++) ^= *(q++); + } + return retval; } /************************************************************************** @@ -335,673 +332,656 @@ xor(unsigned char *p, unsigned char *q, int length) /* * get a pseudo-random vector. */ -static void -get_random_vector(unsigned char *vector) +static void get_random_vector(unsigned char *vector) { #ifdef linux - int fd = open("/dev/urandom",O_RDONLY); /* Linux: get *real* random numbers */ - int total = 0; - if (fd >= 0) { - while (total < AUTH_VECTOR_LEN) { - int bytes = read(fd, vector + total, AUTH_VECTOR_LEN - total); - if (bytes <= 0) - break; /* oops! Error */ - total += bytes; - } - close(fd); - } - - if (total != AUTH_VECTOR_LEN) + int fd = open("/dev/urandom",O_RDONLY); /* Linux: get *real* random numbers */ + int total = 0; + if (fd >= 0) { + while (total < AUTH_VECTOR_LEN) { + int bytes = read(fd, vector + total, AUTH_VECTOR_LEN - total); + if (bytes <= 0) + break; /* oops! Error */ + total += bytes; + } + close(fd); + } + + if (total != AUTH_VECTOR_LEN) #endif - { /* do this *always* on other platforms */ - MD5_CTX my_md5; - struct timeval tv; - struct timezone tz; - static unsigned int session = 0; /* make the number harder to guess */ - - /* Use the time of day with the best resolution the system can - give us -- often close to microsecond accuracy. */ - gettimeofday(&tv,&tz); - - if (session == 0) { - session = getppid(); /* (possibly) hard to guess information */ - } - - tv.tv_sec ^= getpid() * session++; - - /* Hash things to get maybe cryptographically strong pseudo-random numbers */ - MD5Init(&my_md5); - MD5Update(&my_md5, (unsigned char *) &tv, sizeof(tv)); - MD5Update(&my_md5, (unsigned char *) &tz, sizeof(tz)); - MD5Final(vector, &my_md5); /* set the final vector */ - } + { /* do this *always* on other platforms */ + MD5_CTX my_md5; + struct timeval tv; + struct timezone tz; + static unsigned int session = 0; /* make the number harder to guess */ + + /* Use the time of day with the best resolution the system can + give us -- often close to microsecond accuracy. */ + gettimeofday(&tv,&tz); + + if (session == 0) { + session = getppid(); /* (possibly) hard to guess information */ + } + + tv.tv_sec ^= getpid() * session++; + + /* Hash things to get maybe cryptographically strong pseudo-random numbers */ + MD5Init(&my_md5); + MD5Update(&my_md5, (unsigned char *) &tv, sizeof(tv)); + MD5Update(&my_md5, (unsigned char *) &tz, sizeof(tz)); + MD5Final(vector, &my_md5); /* set the final vector */ + } } /* * RFC 2139 says to do generate the accounting request vector this way. - * However, the Livingston 1.16 server doesn't check it. The Cistron + * However, the Livingston 1.16 server doesn't check it. The Cistron * server (http://home.cistron.nl/~miquels/radius/) does, and this code - * seems to work with it. It also works with Funk's Steel-Belted RADIUS. + * seems to work with it. It also works with Funk's Steel-Belted RADIUS. */ -static void -get_accounting_vector(AUTH_HDR *request, radius_server_t *server) +static void get_accounting_vector(AUTH_HDR *request, radius_server_t *server) { - MD5_CTX my_md5; - int secretlen = strlen(server->secret); - int len = ntohs(request->length); - - memset(request->vector, 0, AUTH_VECTOR_LEN); - MD5Init(&my_md5); - memcpy(((char *)request) + len, server->secret, secretlen); - - MD5Update(&my_md5, (unsigned char *)request, len + secretlen); - MD5Final(request->vector, &my_md5); /* set the final vector */ + MD5_CTX my_md5; + int secretlen = strlen(server->secret); + int len = ntohs(request->length); + + memset(request->vector, 0, AUTH_VECTOR_LEN); + MD5Init(&my_md5); + memcpy(((char *)request) + len, server->secret, secretlen); + + MD5Update(&my_md5, (unsigned char *)request, len + secretlen); + MD5Final(request->vector, &my_md5); /* set the final vector */ } /* * Verify the response from the server */ -static int -verify_packet(char *secret, AUTH_HDR *response, AUTH_HDR *request) +static int verify_packet(char *secret, AUTH_HDR *response, AUTH_HDR *request) { - MD5_CTX my_md5; - unsigned char calculated[AUTH_VECTOR_LEN]; - unsigned char reply[AUTH_VECTOR_LEN]; - - /* - * We could dispense with the memcpy, and do MD5's of the packet - * + vector piece by piece. This is easier understand, and maybe faster. - */ - memcpy(reply, response->vector, AUTH_VECTOR_LEN); /* save the reply */ - memcpy(response->vector, request->vector, AUTH_VECTOR_LEN); /* sent vector */ - - /* MD5(response packet header + vector + response packet data + secret) */ - MD5Init(&my_md5); - MD5Update(&my_md5, (unsigned char *) response, ntohs(response->length)); - - /* - * This next bit is necessary because of a bug in the original Livingston - * RADIUS server. The authentication vector is *supposed* to be MD5'd - * with the old password (as the secret) for password changes. - * However, the old password isn't used. The "authentication" vector - * for the server reply packet is simply the MD5 of the reply packet. - * Odd, the code is 99% there, but the old password is never copied - * to the secret! - */ - if (*secret) { - MD5Update(&my_md5, (unsigned char *) secret, strlen(secret)); - } - - MD5Final(calculated, &my_md5); /* set the final vector */ - - /* Did he use the same random vector + shared secret? */ - if (memcmp(calculated, reply, AUTH_VECTOR_LEN) != 0) { - return FALSE; - } - return TRUE; + MD5_CTX my_md5; + unsigned char calculated[AUTH_VECTOR_LEN]; + unsigned char reply[AUTH_VECTOR_LEN]; + + /* + * We could dispense with the memcpy, and do MD5's of the packet + * + vector piece by piece. This is easier understand, and maybe faster. + */ + memcpy(reply, response->vector, AUTH_VECTOR_LEN); /* save the reply */ + memcpy(response->vector, request->vector, AUTH_VECTOR_LEN); /* sent vector */ + + /* MD5(response packet header + vector + response packet data + secret) */ + MD5Init(&my_md5); + MD5Update(&my_md5, (unsigned char *) response, ntohs(response->length)); + + /* + * This next bit is necessary because of a bug in the original Livingston + * RADIUS server. The authentication vector is *supposed* to be MD5'd + * with the old password (as the secret) for password changes. + * However, the old password isn't used. The "authentication" vector + * for the server reply packet is simply the MD5 of the reply packet. + * Odd, the code is 99% there, but the old password is never copied + * to the secret! + */ + if (*secret) { + MD5Update(&my_md5, (unsigned char *) secret, strlen(secret)); + } + + MD5Final(calculated, &my_md5); /* set the final vector */ + + /* Did he use the same random vector + shared secret? */ + if (memcmp(calculated, reply, AUTH_VECTOR_LEN) != 0) { + return FALSE; + } + return TRUE; } /* - * Find an attribute in a RADIUS packet. Note that the packet length + * Find an attribute in a RADIUS packet. Note that the packet length * is *always* kept in network byte order. */ -static attribute_t * -find_attribute(AUTH_HDR *response, unsigned char type) +static attribute_t *find_attribute(AUTH_HDR *response, unsigned char type) { - attribute_t *attr = (attribute_t *) &response->data; + attribute_t *attr = (attribute_t *) &response->data; - int len = ntohs(response->length) - AUTH_HDR_LEN; + int len = ntohs(response->length) - AUTH_HDR_LEN; - while (attr->attribute != type) { - if ((len -= attr->length) <= 0) { - return NULL; /* not found */ - } - attr = (attribute_t *) ((char *) attr + attr->length); - } + while (attr->attribute != type) { + if ((len -= attr->length) <= 0) { + return NULL; /* not found */ + } + attr = (attribute_t *) ((char *) attr + attr->length); + } - return attr; + return attr; } /* * Add an attribute to a RADIUS packet. */ -static void -add_attribute(AUTH_HDR *request, unsigned char type, CONST unsigned char *data, int length) +static void add_attribute(AUTH_HDR *request, unsigned char type, CONST unsigned char *data, int length) { - attribute_t *p; + attribute_t *p; - p = (attribute_t *) ((unsigned char *)request + ntohs(request->length)); - p->attribute = type; - p->length = length + 2; /* the total size of the attribute */ - request->length = htons(ntohs(request->length) + p->length); - memcpy(p->data, data, length); + p = (attribute_t *) ((unsigned char *)request + ntohs(request->length)); + p->attribute = type; + p->length = length + 2; /* the total size of the attribute */ + request->length = htons(ntohs(request->length) + p->length); + memcpy(p->data, data, length); } /* * Add an integer attribute to a RADIUS packet. */ -static void -add_int_attribute(AUTH_HDR *request, unsigned char type, int data) +static void add_int_attribute(AUTH_HDR *request, unsigned char type, int data) { - int value = htonl(data); - - add_attribute(request, type, (unsigned char *) &value, sizeof(int)); + int value = htonl(data); + + add_attribute(request, type, (unsigned char *) &value, sizeof(int)); } -/* - * Add a RADIUS password attribute to the packet. Some magic is done here. +/* + * Add a RADIUS password attribute to the packet. Some magic is done here. * * If it's an PW_OLD_PASSWORD attribute, it's encrypted using the encrypted * PW_PASSWORD attribute as the initialization vector. * - * If the password attribute already exists, it's over-written. This allows + * If the password attribute already exists, it's over-written. This allows * us to simply call add_password to update the password for different * servers. */ -static void -add_password(AUTH_HDR *request, unsigned char type, CONST char *password, char *secret) +static void add_password(AUTH_HDR *request, unsigned char type, CONST char *password, char *secret) { - MD5_CTX md5_secret, my_md5; - unsigned char misc[AUTH_VECTOR_LEN]; - int i; - int length = strlen(password); - unsigned char hashed[256 + AUTH_PASS_LEN]; /* can't be longer than this */ - unsigned char *vector; - attribute_t *attr; - - if (length > MAXPASS) { /* shorten the password for now */ - length = MAXPASS; - } - - if (length == 0) { - length = AUTH_PASS_LEN; /* 0 maps to 16 */ - } if ((length & (AUTH_PASS_LEN - 1)) != 0) { - length += (AUTH_PASS_LEN - 1); /* round it up */ - length &= ~(AUTH_PASS_LEN - 1); /* chop it off */ - } /* 16*N maps to itself */ - - memset(hashed, 0, length); - memcpy(hashed, password, strlen(password)); - - attr = find_attribute(request, PW_PASSWORD); - - if (type == PW_PASSWORD) { - vector = request->vector; - } else { - vector = attr->data; /* attr CANNOT be NULL here. */ - } - - /* ************************************************************ */ - /* encrypt the password */ - /* password : e[0] = p[0] ^ MD5(secret + vector) */ - MD5Init(&md5_secret); - MD5Update(&md5_secret, (unsigned char *) secret, strlen(secret)); - my_md5 = md5_secret; /* so we won't re-do the hash later */ - MD5Update(&my_md5, vector, AUTH_VECTOR_LEN); - MD5Final(misc, &my_md5); /* set the final vector */ - xor(hashed, misc, AUTH_PASS_LEN); - - /* For each step through, e[i] = p[i] ^ MD5(secret + e[i-1]) */ - for (i = 1; i < (length >> 4); i++) { - my_md5 = md5_secret; /* grab old value of the hash */ - MD5Update(&my_md5, &hashed[(i-1) * AUTH_PASS_LEN], AUTH_PASS_LEN); - MD5Final(misc, &my_md5); /* set the final vector */ - xor(&hashed[i * AUTH_PASS_LEN], misc, AUTH_PASS_LEN); - } - - if (type == PW_OLD_PASSWORD) { - attr = find_attribute(request, PW_OLD_PASSWORD); - } - - if (!attr) { - add_attribute(request, type, hashed, length); - } else { - memcpy(attr->data, hashed, length); /* overwrite the packet */ - } + MD5_CTX md5_secret, my_md5; + unsigned char misc[AUTH_VECTOR_LEN]; + int i; + int length = strlen(password); + unsigned char hashed[256 + AUTH_PASS_LEN]; /* can't be longer than this */ + unsigned char *vector; + attribute_t *attr; + + if (length > MAXPASS) { /* shorten the password for now */ + length = MAXPASS; + } + + if (length == 0) { + length = AUTH_PASS_LEN; /* 0 maps to 16 */ + } if ((length & (AUTH_PASS_LEN - 1)) != 0) { + length += (AUTH_PASS_LEN - 1); /* round it up */ + length &= ~(AUTH_PASS_LEN - 1); /* chop it off */ + } /* 16*N maps to itself */ + + memset(hashed, 0, length); + memcpy(hashed, password, strlen(password)); + + attr = find_attribute(request, PW_PASSWORD); + + if (type == PW_PASSWORD) { + vector = request->vector; + } else { + vector = attr->data; /* attr CANNOT be NULL here. */ + } + + /* ************************************************************ */ + /* encrypt the password */ + /* password : e[0] = p[0] ^ MD5(secret + vector) */ + MD5Init(&md5_secret); + MD5Update(&md5_secret, (unsigned char *) secret, strlen(secret)); + my_md5 = md5_secret; /* so we won't re-do the hash later */ + MD5Update(&my_md5, vector, AUTH_VECTOR_LEN); + MD5Final(misc, &my_md5); /* set the final vector */ + xor(hashed, misc, AUTH_PASS_LEN); + + /* For each step through, e[i] = p[i] ^ MD5(secret + e[i-1]) */ + for (i = 1; i < (length >> 4); i++) { + my_md5 = md5_secret; /* grab old value of the hash */ + MD5Update(&my_md5, &hashed[(i-1) * AUTH_PASS_LEN], AUTH_PASS_LEN); + MD5Final(misc, &my_md5); /* set the final vector */ + xor(&hashed[i * AUTH_PASS_LEN], misc, AUTH_PASS_LEN); + } + + if (type == PW_OLD_PASSWORD) { + attr = find_attribute(request, PW_OLD_PASSWORD); + } + + if (!attr) { + add_attribute(request, type, hashed, length); + } else { + memcpy(attr->data, hashed, length); /* overwrite the packet */ + } } -static void -cleanup(radius_server_t *server) +static void cleanup(radius_server_t *server) { - radius_server_t *next; - - while (server) { - next = server->next; - _pam_drop(server->hostname); - _pam_forget(server->secret); - _pam_drop(server); - server = next; - } + radius_server_t *next; + + while (server) { + next = server->next; + _pam_drop(server->hostname); + _pam_forget(server->secret); + _pam_drop(server); + server = next; + } } /* * allocate and open a local port for communication with the RADIUS * server */ -static int -initialize(radius_conf_t *conf, int accounting) +static int initialize(radius_conf_t *conf, int accounting) { - struct sockaddr salocal; - u_short local_port; - char hostname[BUFFER_SIZE]; - char secret[BUFFER_SIZE]; - - char buffer[BUFFER_SIZE]; - char *p; - FILE *fserver; - radius_server_t *server = NULL; - struct sockaddr_in * s_in; - int timeout; - int line = 0; - - /* the first time around, read the configuration file */ - if ((fserver = fopen (conf_file, "r")) == (FILE*)NULL) { - _pam_log(LOG_ERR, "Could not open configuration file %s: %s\n", - conf_file, strerror(errno)); - return PAM_ABORT; - } - - while (!feof(fserver) && - (fgets (buffer, sizeof(buffer), fserver) != (char*) NULL) && - (!ferror(fserver))) { - line++; - p = buffer; - - /* - * Skip blank lines and whitespace - */ - while (*p && - ((*p == ' ') || (*p == '\t') || - (*p == '\r') || (*p == '\n'))) p++; - - /* - * Nothing, or just a comment. Ignore the line. - */ - if ((!*p) || (*p == '#')) { - continue; - } - - timeout = 3; - if (sscanf(p, "%s %s %d", hostname, secret, &timeout) < 2) { - _pam_log(LOG_ERR, "ERROR reading %s, line %d: Could not read hostname or secret\n", - conf_file, line); - continue; /* invalid line */ - } else { /* read it in and save the data */ - radius_server_t *tmp; - - tmp = malloc(sizeof(radius_server_t)); - if (server) { - server->next = tmp; - server = server->next; - } else { - conf->server = tmp; - server= tmp; /* first time */ - } - - /* sometime later do memory checks here */ - server->hostname = strdup(hostname); - server->secret = strdup(secret); - server->accounting = accounting; - server->port = 0; - - if ((timeout < 1) || (timeout > 60)) { - server->timeout = 3; - } else { - server->timeout = timeout; - } - server->next = NULL; - } - } - fclose(fserver); - - if (!server) { /* no server found, die a horrible death */ - _pam_log(LOG_ERR, "No RADIUS server found in configuration file %s\n", - conf_file); - return PAM_AUTHINFO_UNAVAIL; - } - - /* open a socket. Dies if it fails */ - conf->sockfd = socket(AF_INET, SOCK_DGRAM, 0); - if (conf->sockfd < 0) { - _pam_log(LOG_ERR, "Failed to open RADIUS socket: %s\n", strerror(errno)); - return PAM_AUTHINFO_UNAVAIL; - } - - /* set up the local end of the socket communications */ - s_in = (struct sockaddr_in *) &salocal; - memset ((char *) s_in, '\0', sizeof(struct sockaddr)); - s_in->sin_family = AF_INET; - s_in->sin_addr.s_addr = INADDR_ANY; - - /* - * Use our process ID as a local port for RADIUS. - */ - local_port = (getpid() & 0x7fff) + 1024; - do { - local_port++; - s_in->sin_port = htons(local_port); - } while ((bind(conf->sockfd, &salocal, sizeof (struct sockaddr_in)) < 0) && - (local_port < 64000)); - - if (local_port >= 64000) { - close(conf->sockfd); - _pam_log(LOG_ERR, "No open port we could bind to."); - return PAM_AUTHINFO_UNAVAIL; - } - - return PAM_SUCCESS; + struct sockaddr salocal; + u_short local_port; + char hostname[BUFFER_SIZE]; + char secret[BUFFER_SIZE]; + + char buffer[BUFFER_SIZE]; + char *p; + FILE *fserver; + radius_server_t *server = NULL; + struct sockaddr_in * s_in; + int timeout; + int line = 0; + + /* the first time around, read the configuration file */ + if ((fserver = fopen (conf_file, "r")) == (FILE*)NULL) { + _pam_log(LOG_ERR, "Could not open configuration file %s: %s\n", + conf_file, strerror(errno)); + return PAM_ABORT; + } + + while (!feof(fserver) && (fgets (buffer, sizeof(buffer), fserver) != (char*) NULL) && (!ferror(fserver))) { + line++; + p = buffer; + + /* + * Skip blank lines and whitespace + */ + while (*p && ((*p == ' ') || (*p == '\t') || (*p == '\r') || (*p == '\n'))) { + p++; + } + + /* + * Nothing, or just a comment. Ignore the line. + */ + if ((!*p) || (*p == '#')) { + continue; + } + + timeout = 3; + if (sscanf(p, "%s %s %d", hostname, secret, &timeout) < 2) { + _pam_log(LOG_ERR, "ERROR reading %s, line %d: Could not read hostname or secret\n", + conf_file, line); + continue; /* invalid line */ + } else { /* read it in and save the data */ + radius_server_t *tmp; + + tmp = malloc(sizeof(radius_server_t)); + if (server) { + server->next = tmp; + server = server->next; + } else { + conf->server = tmp; + server= tmp; /* first time */ + } + + /* sometime later do memory checks here */ + server->hostname = strdup(hostname); + server->secret = strdup(secret); + server->accounting = accounting; + server->port = 0; + + if ((timeout < 1) || (timeout > 60)) { + server->timeout = 3; + } else { + server->timeout = timeout; + } + server->next = NULL; + } + } + fclose(fserver); + + if (!server) { /* no server found, die a horrible death */ + _pam_log(LOG_ERR, "No RADIUS server found in configuration file %s\n", + conf_file); + return PAM_AUTHINFO_UNAVAIL; + } + + /* open a socket. Dies if it fails */ + conf->sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (conf->sockfd < 0) { + _pam_log(LOG_ERR, "Failed to open RADIUS socket: %s\n", strerror(errno)); + return PAM_AUTHINFO_UNAVAIL; + } + + /* set up the local end of the socket communications */ + s_in = (struct sockaddr_in *) &salocal; + memset ((char *) s_in, '\0', sizeof(struct sockaddr)); + s_in->sin_family = AF_INET; + s_in->sin_addr.s_addr = INADDR_ANY; + + /* + * Use our process ID as a local port for RADIUS. + */ + local_port = (getpid() & 0x7fff) + 1024; + do { + local_port++; + s_in->sin_port = htons(local_port); + } while ((bind(conf->sockfd, &salocal, sizeof (struct sockaddr_in)) < 0) && (local_port < 64000)); + + if (local_port >= 64000) { + close(conf->sockfd); + _pam_log(LOG_ERR, "No open port we could bind to."); + return PAM_AUTHINFO_UNAVAIL; + } + + return PAM_SUCCESS; } /* * Helper function for building a radius packet. * It initializes *some* of the header, and adds common attributes. */ -static void -build_radius_packet(AUTH_HDR *request, CONST char *user, CONST char *password, radius_conf_t *conf) +static void build_radius_packet(AUTH_HDR *request, CONST char *user, CONST char *password, radius_conf_t *conf) { - char hostname[256]; - UINT4 ipaddr; - - hostname[0] = '\0'; - gethostname(hostname, sizeof(hostname) - 1); - - request->length = htons(AUTH_HDR_LEN); - - if (password) { /* make a random authentication req vector */ - get_random_vector(request->vector); - } - - add_attribute(request, PW_USER_NAME, (unsigned char *) user, strlen(user)); - - /* - * Add a password, if given. - */ - if (password) { - add_password(request, PW_PASSWORD, password, conf->server->secret); - - /* - * Add a NULL password to non-accounting requests. - */ - } else if (request->code != PW_ACCOUNTING_REQUEST) { - add_password(request, PW_PASSWORD, "", conf->server->secret); - } - - /* the packet is from localhost if on localhost, to make configs easier */ - if ((conf->server->ip.s_addr == ntohl(0x7f000001)) || (!hostname[0])) { - ipaddr = 0x7f000001; - } else { - struct hostent *hp; - - if ((hp = gethostbyname(hostname)) == (struct hostent *) NULL) { - ipaddr = 0x00000000; /* no client IP address */ - } else { - ipaddr = ntohl(*(UINT4 *) hp->h_addr); /* use the first one available */ - } - } - - /* If we can't find an IP address, then don't add one */ - if (ipaddr) { - add_int_attribute(request, PW_NAS_IP_ADDRESS, ipaddr); - } - - /* There's always a NAS identifier */ - if (conf->client_id && *conf->client_id) { - add_attribute(request, PW_NAS_IDENTIFIER, (unsigned char *) conf->client_id, - strlen(conf->client_id)); - } - - /* - * Add in the port (pid) and port type (virtual). - * - * We might want to give the TTY name here, too. - */ - add_int_attribute(request, PW_NAS_PORT_ID, getpid()); - add_int_attribute(request, PW_NAS_PORT_TYPE, PW_NAS_PORT_TYPE_VIRTUAL); + char hostname[256]; + UINT4 ipaddr; + + hostname[0] = '\0'; + gethostname(hostname, sizeof(hostname) - 1); + + request->length = htons(AUTH_HDR_LEN); + + if (password) { /* make a random authentication req vector */ + get_random_vector(request->vector); + } + + add_attribute(request, PW_USER_NAME, (unsigned char *) user, strlen(user)); + + /* + * Add a password, if given. + */ + if (password) { + add_password(request, PW_PASSWORD, password, conf->server->secret); + + /* + * Add a NULL password to non-accounting requests. + */ + } else if (request->code != PW_ACCOUNTING_REQUEST) { + add_password(request, PW_PASSWORD, "", conf->server->secret); + } + + /* the packet is from localhost if on localhost, to make configs easier */ + if ((conf->server->ip.s_addr == ntohl(0x7f000001)) || (!hostname[0])) { + ipaddr = 0x7f000001; + } else { + struct hostent *hp; + + if ((hp = gethostbyname(hostname)) == (struct hostent *) NULL) { + ipaddr = 0x00000000; /* no client IP address */ + } else { + ipaddr = ntohl(*(UINT4 *) hp->h_addr); /* use the first one available */ + } + } + + /* If we can't find an IP address, then don't add one */ + if (ipaddr) { + add_int_attribute(request, PW_NAS_IP_ADDRESS, ipaddr); + } + + /* There's always a NAS identifier */ + if (conf->client_id && *conf->client_id) { + add_attribute(request, PW_NAS_IDENTIFIER, (unsigned char *) conf->client_id, strlen(conf->client_id)); + } + + /* + * Add in the port (pid) and port type (virtual). + * + * We might want to give the TTY name here, too. + */ + add_int_attribute(request, PW_NAS_PORT_ID, getpid()); + add_int_attribute(request, PW_NAS_PORT_TYPE, PW_NAS_PORT_TYPE_VIRTUAL); } /* * Talk RADIUS to a server. * Send a packet and get the response */ -static int -talk_radius(radius_conf_t *conf, AUTH_HDR *request, AUTH_HDR *response, - char *password, char *old_password, int tries) +static int talk_radius(radius_conf_t *conf, AUTH_HDR *request, AUTH_HDR *response, + char *password, char *old_password, int tries) { - socklen_t salen; - int total_length; - fd_set set; - struct timeval tv; - time_t now, end; - int rcode; - struct sockaddr saremote; - struct sockaddr_in *s_in = (struct sockaddr_in *) &saremote; - radius_server_t *server = conf->server; - int ok; - int server_tries; - int retval; - - /* ************************************************************ */ - /* Now that we're done building the request, we can send it */ - - /* - Hmm... on password change requests, all of the found server information - could be saved with a pam_set_data(), which means even the radius_conf_t - information will have to be malloc'd at some point - - On the other hand, we could just try all of the servers again in - sequence, on the off chance that one may have ended up fixing itself. - - */ - - /* loop over all available servers */ - while (server != NULL) { - - /* only look up IP information as necessary */ - if ((retval = host2server(server)) != PAM_SUCCESS) { - _pam_log(LOG_ERR, - "Failed looking up IP address for RADIUS server %s (errcode=%d)", - server->hostname, retval); - ok = FALSE; - goto next; /* skip to the next server */ - } - - /* set up per-server IP && port configuration */ - memset ((char *) s_in, '\0', sizeof(struct sockaddr)); - s_in->sin_family = AF_INET; - s_in->sin_addr.s_addr = htonl(server->ip.s_addr); - s_in->sin_port = server->port; - total_length = ntohs(request->length); - - if (!password) { /* make an RFC 2139 p6 request authenticator */ - get_accounting_vector(request, server); - } - - server_tries = tries; -send: - /* send the packet */ - if (sendto(conf->sockfd, (char *) request, total_length, 0, - &saremote, sizeof(struct sockaddr_in)) < 0) { - _pam_log(LOG_ERR, "Error sending RADIUS packet to server %s: %s", - server->hostname, strerror(errno)); - ok = FALSE; - goto next; /* skip to the next server */ - } - - /* ************************************************************ */ - /* Wait for the response, and verify it. */ - salen = sizeof(struct sockaddr); - tv.tv_sec = server->timeout; /* wait for the specified time */ - tv.tv_usec = 0; - FD_ZERO(&set); /* clear out the set */ - FD_SET(conf->sockfd, &set); /* wait only for the RADIUS UDP socket */ - - time(&now); - end = now + tv.tv_sec; - - /* loop, waiting for the select to return data */ - ok = TRUE; - while (ok) { - - rcode = select(conf->sockfd + 1, &set, NULL, NULL, &tv); - - /* select timed out */ - if (rcode == 0) { - _pam_log(LOG_ERR, "RADIUS server %s failed to respond", - server->hostname); - if (--server_tries) - goto send; - ok = FALSE; - break; /* exit from the select loop */ - } else if (rcode < 0) { - - /* select had an error */ - if (errno == EINTR) { /* we were interrupted */ - time(&now); - - if (now > end) { - _pam_log(LOG_ERR, "RADIUS server %s failed to respond", - server->hostname); - if (--server_tries) - goto send; - ok = FALSE; - break; /* exit from the select loop */ - } - - tv.tv_sec = end - now; - if (tv.tv_sec == 0) { /* keep waiting */ - tv.tv_sec = 1; - } - - } else { /* not an interrupt, it was a real error */ - _pam_log(LOG_ERR, "Error waiting for response from RADIUS server %s: %s", - server->hostname, strerror(errno)); - ok = FALSE; - break; - } + socklen_t salen; + int total_length; + fd_set set; + struct timeval tv; + time_t now, end; + int rcode; + struct sockaddr saremote; + struct sockaddr_in *s_in = (struct sockaddr_in *) &saremote; + radius_server_t *server = conf->server; + int ok; + int server_tries; + int retval; + + /* ************************************************************ */ + /* Now that we're done building the request, we can send it */ + + /* + Hmm... on password change requests, all of the found server information + could be saved with a pam_set_data(), which means even the radius_conf_t + information will have to be malloc'd at some point - /* the select returned OK */ - } else if (FD_ISSET(conf->sockfd, &set)) { + On the other hand, we could just try all of the servers again in + sequence, on the off chance that one may have ended up fixing itself. - /* try to receive some data */ - if ((total_length = recvfrom(conf->sockfd, (void *) response, - BUFFER_SIZE, - 0, &saremote, &salen)) < 0) { - _pam_log(LOG_ERR, "error reading RADIUS packet from server %s: %s", - server->hostname, strerror(errno)); - ok = FALSE; - break; + */ - /* there's data, see if it's valid */ - } else { - char *p = server->secret; - - if ((ntohs(response->length) != total_length) || - (ntohs(response->length) > BUFFER_SIZE)) { - _pam_log(LOG_ERR, "RADIUS packet from server %s is corrupted", - server->hostname); - ok = FALSE; - break; - } - - /* Check if we have the data OK. We should also check request->id */ - - if (password) { - if (old_password) { + /* loop over all available servers */ + while (server != NULL) { + + /* only look up IP information as necessary */ + if ((retval = host2server(server)) != PAM_SUCCESS) { + _pam_log(LOG_ERR, + "Failed looking up IP address for RADIUS server %s (errcode=%d)", + server->hostname, retval); + ok = FALSE; + goto next; /* skip to the next server */ + } + + /* set up per-server IP && port configuration */ + memset ((char *) s_in, '\0', sizeof(struct sockaddr)); + s_in->sin_family = AF_INET; + s_in->sin_addr.s_addr = htonl(server->ip.s_addr); + s_in->sin_port = server->port; + total_length = ntohs(request->length); + + if (!password) { /* make an RFC 2139 p6 request authenticator */ + get_accounting_vector(request, server); + } + + server_tries = tries; + send: + /* send the packet */ + if (sendto(conf->sockfd, (char *) request, total_length, 0, + &saremote, sizeof(struct sockaddr_in)) < 0) { + _pam_log(LOG_ERR, "Error sending RADIUS packet to server %s: %s", + server->hostname, strerror(errno)); + ok = FALSE; + goto next; /* skip to the next server */ + } + + /* ************************************************************ */ + /* Wait for the response, and verify it. */ + salen = sizeof(struct sockaddr); + tv.tv_sec = server->timeout; /* wait for the specified time */ + tv.tv_usec = 0; + FD_ZERO(&set); /* clear out the set */ + FD_SET(conf->sockfd, &set); /* wait only for the RADIUS UDP socket */ + + time(&now); + end = now + tv.tv_sec; + + /* loop, waiting for the select to return data */ + ok = TRUE; + while (ok) { + rcode = select(conf->sockfd + 1, &set, NULL, NULL, &tv); + + /* select timed out */ + if (rcode == 0) { + _pam_log(LOG_ERR, "RADIUS server %s failed to respond", server->hostname); + if (--server_tries) { + goto send; + } + ok = FALSE; + break; /* exit from the select loop */ + } else if (rcode < 0) { + + /* select had an error */ + if (errno == EINTR) { /* we were interrupted */ + time(&now); + + if (now > end) { + _pam_log(LOG_ERR, "RADIUS server %s failed to respond", + server->hostname); + if (--server_tries) goto send; + ok = FALSE; + break; /* exit from the select loop */ + } + + tv.tv_sec = end - now; + if (tv.tv_sec == 0) { /* keep waiting */ + tv.tv_sec = 1; + } + + } else { /* not an interrupt, it was a real error */ + _pam_log(LOG_ERR, "Error waiting for response from RADIUS server %s: %s", + server->hostname, strerror(errno)); + ok = FALSE; + break; + } + + /* the select returned OK */ + } else if (FD_ISSET(conf->sockfd, &set)) { + + /* try to receive some data */ + if ((total_length = recvfrom(conf->sockfd, (void *) response, BUFFER_SIZE, + 0, &saremote, &salen)) < 0) { + _pam_log(LOG_ERR, "error reading RADIUS packet from server %s: %s", + server->hostname, strerror(errno)); + ok = FALSE; + break; + + /* there's data, see if it's valid */ + } else { + char *p = server->secret; + + if ((ntohs(response->length) != total_length) || + (ntohs(response->length) > BUFFER_SIZE)) { + _pam_log(LOG_ERR, "RADIUS packet from server %s is corrupted", + server->hostname); + ok = FALSE; + break; + } + + /* Check if we have the data OK. We should also check request->id */ + if (password) { + if (old_password) { #ifdef LIVINGSTON_PASSWORD_VERIFY_BUG_FIXED - p = old_password; /* what it should be */ + p = old_password; /* what it should be */ #else - p = ""; /* what it really is */ + p = ""; /* what it really is */ #endif - } - /* - * RFC 2139 p.6 says not do do this, but the Livingston 1.16 - * server disagrees. If the user says he wants the bug, give in. - */ - } else { /* authentication request */ - if (conf->accounting_bug) { - p = ""; - } - } - - if (!verify_packet(p, response, request)) { - _pam_log(LOG_ERR, "packet from RADIUS server %s fails verification: The shared secret is probably incorrect.", - server->hostname); - ok = FALSE; - break; - } - - /* - * Check that the response ID matches the request ID. - */ - if (response->id != request->id) { - _pam_log(LOG_WARNING, "Response packet ID %d does not match the request packet ID %d: verification of packet fails", response->id, request->id); - ok = FALSE; - break; - } + } + /* + * RFC 2139 p.6 says not do do this, but the Livingston 1.16 + * server disagrees. If the user says he wants the bug, give in. + */ + } else { /* authentication request */ + if (conf->accounting_bug) { + p = ""; + } + } + + if (!verify_packet(p, response, request)) { + _pam_log(LOG_ERR, "packet from RADIUS server %s failed verification: " + "The shared secret is probably incorrect.", server->hostname); + ok = FALSE; + break; + } + + /* + * Check that the response ID matches the request ID. + */ + if (response->id != request->id) { + _pam_log(LOG_WARNING, "Response packet ID %d does not match the " + "request packet ID %d: verification of packet fails", + response->id, request->id); + ok = FALSE; + break; + } + } + + /* + * Whew! The select is done. It hasn't timed out, or errored out. + * It's our descriptor. We've got some data. It's the right size. + * The packet is valid. + * NOW, we can skip out of the select loop, and process the packet + */ + break; + } + /* otherwise, we've got data on another descriptor, keep select'ing */ + } + + /* go to the next server if this one didn't respond */ + next: + if (!ok) { + radius_server_t *old; /* forget about this server */ + + old = server; + server = server->next; + conf->server = server; + + _pam_forget(old->secret); + free(old->hostname); + free(old); + + if (server) { /* if there's more servers to check */ + /* get a new authentication vector, and update the passwords */ + get_random_vector(request->vector); + request->id = request->vector[0]; + + /* update passwords, as appropriate */ + if (password) { + get_random_vector(request->vector); + if (old_password) { /* password change request */ + add_password(request, PW_PASSWORD, password, old_password); + add_password(request, PW_OLD_PASSWORD, old_password, old_password); + } else { /* authentication request */ + add_password(request, PW_PASSWORD, password, server->secret); + } + } + } + continue; + + } else { + /* we've found one that does respond, forget about the other servers */ + cleanup(server->next); + server->next = NULL; + live_server = server; /* we've got a live one! */ + break; + } } - - /* - * Whew! The select is done. It hasn't timed out, or errored out. - * It's our descriptor. We've got some data. It's the right size. - * The packet is valid. - * NOW, we can skip out of the select loop, and process the packet - */ - break; - } - /* otherwise, we've got data on another descriptor, keep select'ing */ - } - - /* go to the next server if this one didn't respond */ - next: - if (!ok) { - radius_server_t *old; /* forget about this server */ - - old = server; - server = server->next; - conf->server = server; - - _pam_forget(old->secret); - free(old->hostname); - free(old); - - if (server) { /* if there's more servers to check */ - /* get a new authentication vector, and update the passwords */ - get_random_vector(request->vector); - request->id = request->vector[0]; - - /* update passwords, as appropriate */ - if (password) { - get_random_vector(request->vector); - if (old_password) { /* password change request */ - add_password(request, PW_PASSWORD, password, old_password); - add_password(request, PW_OLD_PASSWORD, old_password, old_password); - } else { /* authentication request */ - add_password(request, PW_PASSWORD, password, server->secret); - } + + if (!server) { + _pam_log(LOG_ERR, "All RADIUS servers failed to respond."); + if (conf->localifdown) + retval = PAM_IGNORE; + else + retval = PAM_AUTHINFO_UNAVAIL; + } else { + retval = PAM_SUCCESS; } - } - continue; - - } else { - /* we've found one that does respond, forget about the other servers */ - cleanup(server->next); - server->next = NULL; - live_server = server; /* we've got a live one! */ - break; - } - } - - if (!server) { - _pam_log(LOG_ERR, "All RADIUS servers failed to respond."); - if (conf->localifdown) - retval = PAM_IGNORE; - else - retval = PAM_AUTHINFO_UNAVAIL; - } else { - retval = PAM_SUCCESS; - } - - return retval; + + return retval; } /************************************************************************** @@ -1015,37 +995,37 @@ send: static int rad_converse(pam_handle_t *pamh, int msg_style, char *message, char **password) { - CONST struct pam_conv *conv; - struct pam_message resp_msg; - CONST struct pam_message *msg[1]; - struct pam_response *resp = NULL; - int retval; - - resp_msg.msg_style = msg_style; - resp_msg.msg = message; - msg[0] = &resp_msg; - - /* grab the password */ - retval = pam_get_item(pamh, PAM_CONV, (CONST void **) &conv); - PAM_FAIL_CHECK; - - retval = conv->conv(1, msg, &resp,conv->appdata_ptr); - PAM_FAIL_CHECK; - - if (password) { /* assume msg.type needs a response */ - /* I'm not sure if this next bit is necessary on Linux */ + CONST struct pam_conv *conv; + struct pam_message resp_msg; + CONST struct pam_message *msg[1]; + struct pam_response *resp = NULL; + int retval; + + resp_msg.msg_style = msg_style; + resp_msg.msg = message; + msg[0] = &resp_msg; + + /* grab the password */ + retval = pam_get_item(pamh, PAM_CONV, (CONST void **) &conv); + PAM_FAIL_CHECK; + + retval = conv->conv(1, msg, &resp,conv->appdata_ptr); + PAM_FAIL_CHECK; + + if (password) { /* assume msg.type needs a response */ + /* I'm not sure if this next bit is necessary on Linux */ #ifdef sun - /* NULL response, fail authentication */ - if ((resp == NULL) || (resp->resp == NULL)) { - return PAM_SYSTEM_ERR; - } + /* NULL response, fail authentication */ + if ((resp == NULL) || (resp->resp == NULL)) { + return PAM_SYSTEM_ERR; + } #endif - - *password = resp->resp; - free(resp); - } - - return PAM_SUCCESS; + + *password = resp->resp; + free(resp); + } + + return PAM_SUCCESS; } /************************************************************************** @@ -1053,574 +1033,555 @@ static int rad_converse(pam_handle_t *pamh, int msg_style, char *message, char * **************************************************************************/ #undef PAM_FAIL_CHECK -#define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) { \ - int *pret = malloc( sizeof(int) ); \ - *pret = retval; \ - pam_set_data( pamh, "rad_setcred_return" \ - , (void *) pret, _int_free ); \ +#define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) { \ + int *pret = malloc(sizeof(int)); \ + *pret = retval; \ + pam_set_data(pamh, "rad_setcred_return", (void *) pret, _int_free); \ return retval; } -PAM_EXTERN int -pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,CONST char **argv) +PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh,int flags,int argc,CONST char **argv) { - CONST char *user; - CONST char *userinfo; - char *password = NULL; - CONST char *rhost; - char *resp2challenge = NULL; - int ctrl; - int retval = PAM_AUTH_ERR; - - char recv_buffer[4096]; - char send_buffer[4096]; - AUTH_HDR *request = (AUTH_HDR *) send_buffer; - AUTH_HDR *response = (AUTH_HDR *) recv_buffer; - radius_conf_t config; - - ctrl = _pam_parse(argc, argv, &config); - - /* grab the user name */ - retval = pam_get_user(pamh, &user, NULL); - PAM_FAIL_CHECK; - - /* check that they've entered something, and not too long, either */ - if ((user == NULL) || - (strlen(user) > MAXPWNAM)) { - int *pret = malloc( sizeof(int) ); - *pret = PAM_USER_UNKNOWN; - pam_set_data( pamh, "rad_setcred_return", (void *) pret, _int_free ); - - DPRINT(LOG_DEBUG, "User name was NULL, or too long"); - return PAM_USER_UNKNOWN; - } - DPRINT(LOG_DEBUG, "Got user name %s", user); - - if (ctrl & PAM_RUSER_ARG) { - retval = pam_get_item(pamh, PAM_RUSER, (CONST void **) &userinfo); - PAM_FAIL_CHECK; - DPRINT(LOG_DEBUG, "Got PAM_RUSER name %s", userinfo); - - if (!strcmp("root", user)) { - user = userinfo; - DPRINT(LOG_DEBUG, "Username now %s from ruser", user); - } else { - DPRINT(LOG_DEBUG, "Skipping ruser for non-root auth"); - }; - }; - - /* - * Get the IP address of the authentication server - * Then, open a socket, and bind it to a port - */ - retval = initialize(&config, FALSE); - PAM_FAIL_CHECK; - - /* - * If there's no client id specified, use the service type, to help - * keep track of which service is doing the authentication. - */ - if (!config.client_id) { - retval = pam_get_item(pamh, PAM_SERVICE, (CONST void **) &config.client_id); - PAM_FAIL_CHECK; - } - - /* now we've got a socket open, so we've got to clean it up on error */ + CONST char *user; + CONST char *userinfo; + char *password = NULL; + CONST char *rhost; + char *resp2challenge = NULL; + int ctrl; + int retval = PAM_AUTH_ERR; + + char recv_buffer[4096]; + char send_buffer[4096]; + AUTH_HDR *request = (AUTH_HDR *) send_buffer; + AUTH_HDR *response = (AUTH_HDR *) recv_buffer; + radius_conf_t config; + + ctrl = _pam_parse(argc, argv, &config); + + /* grab the user name */ + retval = pam_get_user(pamh, &user, NULL); + PAM_FAIL_CHECK; + + /* check that they've entered something, and not too long, either */ + if ((user == NULL) || (strlen(user) > MAXPWNAM)) { + int *pret = malloc(sizeof(int)); + *pret = PAM_USER_UNKNOWN; + pam_set_data(pamh, "rad_setcred_return", (void *) pret, _int_free); + + DPRINT(LOG_DEBUG, "User name was NULL, or too long"); + return PAM_USER_UNKNOWN; + } + DPRINT(LOG_DEBUG, "Got user name %s", user); + + if (ctrl & PAM_RUSER_ARG) { + retval = pam_get_item(pamh, PAM_RUSER, (CONST void **) &userinfo); + PAM_FAIL_CHECK; + DPRINT(LOG_DEBUG, "Got PAM_RUSER name %s", userinfo); + + if (!strcmp("root", user)) { + user = userinfo; + DPRINT(LOG_DEBUG, "Username now %s from ruser", user); + } else { + DPRINT(LOG_DEBUG, "Skipping ruser for non-root auth"); + }; + }; + + /* + * Get the IP address of the authentication server + * Then, open a socket, and bind it to a port + */ + retval = initialize(&config, FALSE); + PAM_FAIL_CHECK; + + /* + * If there's no client id specified, use the service type, to help + * keep track of which service is doing the authentication. + */ + if (!config.client_id) { + retval = pam_get_item(pamh, PAM_SERVICE, (CONST void **) &config.client_id); + PAM_FAIL_CHECK; + } + + /* now we've got a socket open, so we've got to clean it up on error */ #undef PAM_FAIL_CHECK #define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) {goto error; } - /* build and initialize the RADIUS packet */ - request->code = PW_AUTHENTICATION_REQUEST; - get_random_vector(request->vector); - request->id = request->vector[0]; /* this should be evenly distributed */ - - /* grab the password (if any) from the previous authentication layer */ - retval = pam_get_item(pamh, PAM_AUTHTOK, (CONST void **) &password); - PAM_FAIL_CHECK; - - if(password) { - password = strdup(password); - DPRINT(LOG_DEBUG, "Got password %s", password); - } - - /* no previous password: maybe get one from the user */ - if (!password) { - if (ctrl & PAM_USE_FIRST_PASS) { - retval = PAM_AUTH_ERR; /* use one pass only, stopping if it fails */ - goto error; - } - - /* check to see if we send a NULL password the first time around */ - if (!(ctrl & PAM_SKIP_PASSWD)) { - retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, "Password: ", &password); - PAM_FAIL_CHECK; - - } - } /* end of password == NULL */ - - build_radius_packet(request, user, password, &config); - /* not all servers understand this service type, but some do */ - add_int_attribute(request, PW_USER_SERVICE_TYPE, PW_AUTHENTICATE_ONLY); - - /* - * Tell the server which host the user is coming from. - * - * Note that this is NOT the IP address of the machine running PAM! - * It's the IP address of the client. - */ - retval = pam_get_item(pamh, PAM_RHOST, (CONST void **) &rhost); - PAM_FAIL_CHECK; - if (rhost) { - add_attribute(request, PW_CALLING_STATION_ID, (unsigned char *) rhost, - strlen(rhost)); - } - - DPRINT(LOG_DEBUG, "Sending RADIUS request code %d", request->code); - - retval = talk_radius(&config, request, response, password, - NULL, config.retries + 1); - PAM_FAIL_CHECK; - - DPRINT(LOG_DEBUG, "Got RADIUS response code %d", response->code); - - /* - * If we get an authentication failure, and we sent a NULL password, - * ask the user for one and continue. - * - * If we get an access challenge, then do a response, for as many - * challenges as we receive. - */ - while (response->code == PW_ACCESS_CHALLENGE) { - attribute_t *a_state, *a_reply; - char challenge[BUFFER_SIZE]; - - /* Now we do a bit more work: challenge the user, and get a response */ - if (((a_state = find_attribute(response, PW_STATE)) == NULL) || - ((a_reply = find_attribute(response, PW_REPLY_MESSAGE)) == NULL)) { - /* Actually, State isn't required. */ - _pam_log(LOG_ERR, "RADIUS Access-Challenge received with State or Reply-Message missing"); - retval = PAM_AUTHINFO_UNAVAIL; - goto error; - } - - /* - * Security fixes. - */ - if ((a_state->length <= 2) || (a_reply->length <= 2)) { - _pam_log(LOG_ERR, "RADIUS Access-Challenge received with invalid State or Reply-Message"); - retval = PAM_AUTHINFO_UNAVAIL; - goto error; - } - - memcpy(challenge, a_reply->data, a_reply->length - 2); - challenge[a_reply->length - 2] = 0; - - /* It's full challenge-response, we should have echo on */ - retval = rad_converse(pamh, PAM_PROMPT_ECHO_ON, challenge, &resp2challenge); - - /* now that we've got a response, build a new radius packet */ - build_radius_packet(request, user, resp2challenge, &config); - /* request->code is already PW_AUTHENTICATION_REQUEST */ - request->id++; /* one up from the request */ - - /* copy the state over from the servers response */ - add_attribute(request, PW_STATE, a_state->data, a_state->length - 2); - - retval = talk_radius(&config, request, response, resp2challenge, NULL, 1); - PAM_FAIL_CHECK; - - DPRINT(LOG_DEBUG, "Got response to challenge code %d", response->code); - } - - /* Whew! Done the pasword checks, look for an authentication acknowledge */ - if (response->code == PW_AUTHENTICATION_ACK) { - retval = PAM_SUCCESS; - } else { - retval = PAM_AUTH_ERR; /* authentication failure */ + /* build and initialize the RADIUS packet */ + request->code = PW_AUTHENTICATION_REQUEST; + get_random_vector(request->vector); + request->id = request->vector[0]; /* this should be evenly distributed */ + + /* grab the password (if any) from the previous authentication layer */ + retval = pam_get_item(pamh, PAM_AUTHTOK, (CONST void **) &password); + PAM_FAIL_CHECK; + + if(password) { + password = strdup(password); + DPRINT(LOG_DEBUG, "Got password %s", password); + } + + /* no previous password: maybe get one from the user */ + if (!password) { + if (ctrl & PAM_USE_FIRST_PASS) { + retval = PAM_AUTH_ERR; /* use one pass only, stopping if it fails */ + goto error; + } + + /* check to see if we send a NULL password the first time around */ + if (!(ctrl & PAM_SKIP_PASSWD)) { + retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, "Password: ", &password); + PAM_FAIL_CHECK; + + } + } /* end of password == NULL */ + + build_radius_packet(request, user, password, &config); + /* not all servers understand this service type, but some do */ + add_int_attribute(request, PW_USER_SERVICE_TYPE, PW_AUTHENTICATE_ONLY); + + /* + * Tell the server which host the user is coming from. + * + * Note that this is NOT the IP address of the machine running PAM! + * It's the IP address of the client. + */ + retval = pam_get_item(pamh, PAM_RHOST, (CONST void **) &rhost); + PAM_FAIL_CHECK; + if (rhost) { + add_attribute(request, PW_CALLING_STATION_ID, (unsigned char *) rhost, + strlen(rhost)); + } + + DPRINT(LOG_DEBUG, "Sending RADIUS request code %d", request->code); + + retval = talk_radius(&config, request, response, password, NULL, config.retries + 1); + PAM_FAIL_CHECK; + + DPRINT(LOG_DEBUG, "Got RADIUS response code %d", response->code); + + /* + * If we get an authentication failure, and we sent a NULL password, + * ask the user for one and continue. + * + * If we get an access challenge, then do a response, for as many + * challenges as we receive. + */ + while (response->code == PW_ACCESS_CHALLENGE) { + attribute_t *a_state, *a_reply; + char challenge[BUFFER_SIZE]; + + /* Now we do a bit more work: challenge the user, and get a response */ + if (((a_state = find_attribute(response, PW_STATE)) == NULL) || + ((a_reply = find_attribute(response, PW_REPLY_MESSAGE)) == NULL)) { + /* Actually, State isn't required. */ + _pam_log(LOG_ERR, "RADIUS Access-Challenge received with State or Reply-Message missing"); + retval = PAM_AUTHINFO_UNAVAIL; + goto error; + } + + /* + * Security fixes. + */ + if ((a_state->length <= 2) || (a_reply->length <= 2)) { + _pam_log(LOG_ERR, "RADIUS Access-Challenge received with invalid State or Reply-Message"); + retval = PAM_AUTHINFO_UNAVAIL; + goto error; + } + + memcpy(challenge, a_reply->data, a_reply->length - 2); + challenge[a_reply->length - 2] = 0; + + /* It's full challenge-response, we should have echo on */ + retval = rad_converse(pamh, PAM_PROMPT_ECHO_ON, challenge, &resp2challenge); + + /* now that we've got a response, build a new radius packet */ + build_radius_packet(request, user, resp2challenge, &config); + /* request->code is already PW_AUTHENTICATION_REQUEST */ + request->id++; /* one up from the request */ + + /* copy the state over from the servers response */ + add_attribute(request, PW_STATE, a_state->data, a_state->length - 2); + + retval = talk_radius(&config, request, response, resp2challenge, NULL, 1); + PAM_FAIL_CHECK; + + DPRINT(LOG_DEBUG, "Got response to challenge code %d", response->code); + } + + /* Whew! Done the pasword checks, look for an authentication acknowledge */ + if (response->code == PW_AUTHENTICATION_ACK) { + retval = PAM_SUCCESS; + } else { + retval = PAM_AUTH_ERR; /* authentication failure */ error: - /* If there was a password pass it to the next layer */ - if (password && *password) { - pam_set_item(pamh, PAM_AUTHTOK, password); - } - } - - if (ctrl & PAM_DEBUG_ARG) { - _pam_log(LOG_DEBUG, "authentication %s" - , retval==PAM_SUCCESS ? "succeeded":"failed" ); - } - - close(config.sockfd); - cleanup(config.server); - _pam_forget(password); - _pam_forget(resp2challenge); - { - int *pret = malloc( sizeof(int) ); - *pret = retval; - pam_set_data( pamh, "rad_setcred_return", (void *) pret, _int_free ); - } - return retval; + /* If there was a password pass it to the next layer */ + if (password && *password) { + pam_set_item(pamh, PAM_AUTHTOK, password); + } + } + + if (ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_DEBUG, "authentication %s", retval==PAM_SUCCESS ? "succeeded":"failed"); + } + + close(config.sockfd); + cleanup(config.server); + _pam_forget(password); + _pam_forget(resp2challenge); + { + int *pret = malloc(sizeof(int)); + *pret = retval; + pam_set_data(pamh, "rad_setcred_return", (void *) pret, _int_free); + } + return retval; } /* * Return a value matching the return value of pam_sm_authenticate, for - * greatest compatibility. + * greatest compatibility. * (Always returning PAM_SUCCESS breaks other authentication modules; * always returning PAM_IGNORE breaks PAM when we're the only module.) */ -PAM_EXTERN int -pam_sm_setcred(pam_handle_t *pamh,int flags,int argc,CONST char **argv) +PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc,CONST char **argv) { - int retval, *pret; + int retval, *pret; - retval = PAM_SUCCESS; - pret = &retval; - pam_get_data( pamh, "rad_setcred_return", (CONST void **) &pret ); - return *pret; + retval = PAM_SUCCESS; + pret = &retval; + pam_get_data(pamh, "rad_setcred_return", (CONST void **) &pret); + return *pret; } #undef PAM_FAIL_CHECK #define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) { return PAM_SESSION_ERR; } -static int -pam_private_session(pam_handle_t *pamh, int flags, - int argc, CONST char **argv, - int status) +static int pam_private_session(pam_handle_t *pamh, int flags, int argc, CONST char **argv, int status) { - CONST char *user; - int ctrl; - int retval = PAM_AUTH_ERR; - - char recv_buffer[4096]; - char send_buffer[4096]; - AUTH_HDR *request = (AUTH_HDR *) send_buffer; - AUTH_HDR *response = (AUTH_HDR *) recv_buffer; - radius_conf_t config; - - ctrl = _pam_parse(argc, argv, &config); - - /* grab the user name */ - retval = pam_get_user(pamh, &user, NULL); - PAM_FAIL_CHECK; - - /* check that they've entered something, and not too long, either */ - if ((user == NULL) || - (strlen(user) > MAXPWNAM)) { - return PAM_USER_UNKNOWN; - } - - /* - * Get the IP address of the authentication server - * Then, open a socket, and bind it to a port - */ - retval = initialize(&config, TRUE); - PAM_FAIL_CHECK; - - /* - * If there's no client id specified, use the service type, to help - * keep track of which service is doing the authentication. - */ - if (!config.client_id) { - retval = pam_get_item(pamh, PAM_SERVICE, (CONST void **) &config.client_id); - PAM_FAIL_CHECK; - } - - /* now we've got a socket open, so we've got to clean it up on error */ + CONST char *user; + int ctrl; + int retval = PAM_AUTH_ERR; + + char recv_buffer[4096]; + char send_buffer[4096]; + AUTH_HDR *request = (AUTH_HDR *) send_buffer; + AUTH_HDR *response = (AUTH_HDR *) recv_buffer; + radius_conf_t config; + + ctrl = _pam_parse(argc, argv, &config); + + /* grab the user name */ + retval = pam_get_user(pamh, &user, NULL); + PAM_FAIL_CHECK; + + /* check that they've entered something, and not too long, either */ + if ((user == NULL) || (strlen(user) > MAXPWNAM)) { + return PAM_USER_UNKNOWN; + } + + /* + * Get the IP address of the authentication server + * Then, open a socket, and bind it to a port + */ + retval = initialize(&config, TRUE); + PAM_FAIL_CHECK; + + /* + * If there's no client id specified, use the service type, to help + * keep track of which service is doing the authentication. + */ + if (!config.client_id) { + retval = pam_get_item(pamh, PAM_SERVICE, (CONST void **) &config.client_id); + PAM_FAIL_CHECK; + } + + /* now we've got a socket open, so we've got to clean it up on error */ #undef PAM_FAIL_CHECK #define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) {goto error; } - /* build and initialize the RADIUS packet */ - request->code = PW_ACCOUNTING_REQUEST; - get_random_vector(request->vector); - request->id = request->vector[0]; /* this should be evenly distributed */ + /* build and initialize the RADIUS packet */ + request->code = PW_ACCOUNTING_REQUEST; + get_random_vector(request->vector); + request->id = request->vector[0]; /* this should be evenly distributed */ - build_radius_packet(request, user, NULL, &config); + build_radius_packet(request, user, NULL, &config); - add_int_attribute(request, PW_ACCT_STATUS_TYPE, status); + add_int_attribute(request, PW_ACCT_STATUS_TYPE, status); - sprintf(recv_buffer, "%08d", (int) getpid()); - add_attribute(request, PW_ACCT_SESSION_ID, (unsigned char *) recv_buffer, - strlen(recv_buffer)); + sprintf(recv_buffer, "%08d", (int) getpid()); + add_attribute(request, PW_ACCT_SESSION_ID, (unsigned char *) recv_buffer, strlen(recv_buffer)); - add_int_attribute(request, PW_ACCT_AUTHENTIC, PW_AUTH_RADIUS); + add_int_attribute(request, PW_ACCT_AUTHENTIC, PW_AUTH_RADIUS); - if (status == PW_STATUS_START) { - session_time = time(NULL); - } else { - add_int_attribute(request, PW_ACCT_SESSION_TIME, time(NULL) - session_time); - } + if (status == PW_STATUS_START) { + session_time = time(NULL); + } else { + add_int_attribute(request, PW_ACCT_SESSION_TIME, time(NULL) - session_time); + } - retval = talk_radius(&config, request, response, NULL, NULL, 1); - PAM_FAIL_CHECK; + retval = talk_radius(&config, request, response, NULL, NULL, 1); + PAM_FAIL_CHECK; - /* oops! They don't have the right password. Complain and die. */ - if (response->code != PW_ACCOUNTING_RESPONSE) { - retval = PAM_PERM_DENIED; - goto error; - } + /* oops! They don't have the right password. Complain and die. */ + if (response->code != PW_ACCOUNTING_RESPONSE) { + retval = PAM_PERM_DENIED; + goto error; + } - retval = PAM_SUCCESS; + retval = PAM_SUCCESS; error: - close(config.sockfd); - cleanup(config.server); + close(config.sockfd); + cleanup(config.server); - return retval; + return retval; } -PAM_EXTERN int -pam_sm_open_session(pam_handle_t *pamh, int flags, - int argc, CONST char **argv) +PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, CONST char **argv) { - return pam_private_session(pamh, flags, argc, argv, PW_STATUS_START); + return pam_private_session(pamh, flags, argc, argv, PW_STATUS_START); } -PAM_EXTERN int -pam_sm_close_session(pam_handle_t *pamh, int flags, - int argc, CONST char **argv) +PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, CONST char **argv) { - return pam_private_session(pamh, flags, argc, argv, PW_STATUS_STOP); + return pam_private_session(pamh, flags, argc, argv, PW_STATUS_STOP); } #undef PAM_FAIL_CHECK #define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) {return retval; } #define MAX_PASSWD_TRIES 3 -PAM_EXTERN int -pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, CONST char **argv) +PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, CONST char **argv) { - CONST char *user; - char *password = NULL; - char *new_password = NULL; - char *check_password = NULL; - int ctrl; - int retval = PAM_AUTHTOK_ERR; - int attempts; - - char recv_buffer[4096]; - char send_buffer[4096]; - AUTH_HDR *request = (AUTH_HDR *) send_buffer; - AUTH_HDR *response = (AUTH_HDR *) recv_buffer; - radius_conf_t config; - - ctrl = _pam_parse(argc, argv, &config); - - /* grab the user name */ - retval = pam_get_user(pamh, &user, NULL); - PAM_FAIL_CHECK; - - /* check that they've entered something, and not too long, either */ - if ((user == NULL) || - (strlen(user) > MAXPWNAM)) { - return PAM_USER_UNKNOWN; - } - - /* - * Get the IP address of the authentication server - * Then, open a socket, and bind it to a port - */ - retval = initialize(&config, FALSE); - PAM_FAIL_CHECK; - - /* - * If there's no client id specified, use the service type, to help - * keep track of which service is doing the authentication. - */ - if (!config.client_id) { - retval = pam_get_item(pamh, PAM_SERVICE, (CONST void **) &config.client_id); - PAM_FAIL_CHECK; - } - - /* now we've got a socket open, so we've got to clean it up on error */ -#undef PAM_FAIL_CHECK -#define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) {goto error; } + CONST char *user; + char *password = NULL; + char *new_password = NULL; + char *check_password = NULL; + int ctrl; + int retval = PAM_AUTHTOK_ERR; + int attempts; + + char recv_buffer[4096]; + char send_buffer[4096]; + AUTH_HDR *request = (AUTH_HDR *) send_buffer; + AUTH_HDR *response = (AUTH_HDR *) recv_buffer; + radius_conf_t config; + + ctrl = _pam_parse(argc, argv, &config); + + /* grab the user name */ + retval = pam_get_user(pamh, &user, NULL); + PAM_FAIL_CHECK; - /* grab the old password (if any) from the previous password layer */ - retval = pam_get_item(pamh, PAM_OLDAUTHTOK, (CONST void **) &password); - PAM_FAIL_CHECK; - if(password) password = strdup(password); - - /* grab the new password (if any) from the previous password layer */ - retval = pam_get_item(pamh, PAM_AUTHTOK, (CONST void **) &new_password); - PAM_FAIL_CHECK; - if(new_password) new_password = strdup(new_password); - - /* preliminary password change checks. */ - if (flags & PAM_PRELIM_CHECK) { - if (!password) { /* no previous password: ask for one */ - retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, "Password: ", &password); - PAM_FAIL_CHECK; - } - - /* - * We now check the password to see if it's the right one. - * If it isn't, we let the user try again. - * Note that RADIUS doesn't have any concept of 'root'. The only way - * that root can change someone's password is to log into the RADIUS - * server, and and change it there. - */ - - /* build and initialize the access request RADIUS packet */ - request->code = PW_AUTHENTICATION_REQUEST; - get_random_vector(request->vector); - request->id = request->vector[0]; /* this should be evenly distributed */ - - build_radius_packet(request, user, password, &config); - add_int_attribute(request, PW_USER_SERVICE_TYPE, PW_AUTHENTICATE_ONLY); - - retval = talk_radius(&config, request, response, password, NULL, 1); - PAM_FAIL_CHECK; - - /* oops! They don't have the right password. Complain and die. */ - if (response->code != PW_AUTHENTICATION_ACK) { - _pam_forget(password); - retval = PAM_PERM_DENIED; - goto error; - } - - /* - * We're now sure it's the right user. - * Ask for their new password, if appropriate - */ - - if (!new_password) { /* not found yet: ask for it */ - int new_attempts; - attempts = 0; - - /* loop, trying to get matching new passwords */ - while (attempts++ < 3) { - - /* loop, trying to get a new password */ - new_attempts = 0; - while (new_attempts++ < 3) { - retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, - "New password: ", &new_password); - PAM_FAIL_CHECK; - - /* the old password may be short. Check it, first. */ - if (strcmp(password, new_password) == 0) { /* are they the same? */ - rad_converse(pamh, PAM_ERROR_MSG, - "You must choose a new password.", NULL); - _pam_forget(new_password); - continue; - } else if (strlen(new_password) < 6) { - rad_converse(pamh, PAM_ERROR_MSG, "it's WAY too short", NULL); - _pam_forget(new_password); - continue; - } - - /* insert crypt password checking here */ - - break; /* the new password is OK */ + /* check that they've entered something, and not too long, either */ + if ((user == NULL) || (strlen(user) > MAXPWNAM)) { + return PAM_USER_UNKNOWN; } - - if (new_attempts >= 3) { /* too many new password attempts: die */ - retval = PAM_AUTHTOK_ERR; - goto error; - } - - /* make sure of the password by asking for verification */ - retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, - "New password (again): ", &check_password); + + /* + * Get the IP address of the authentication server + * Then, open a socket, and bind it to a port + */ + retval = initialize(&config, FALSE); PAM_FAIL_CHECK; - - retval = strcmp(new_password, check_password); - _pam_forget(check_password); - - /* if they don't match, don't pass them to the next module */ - if (retval != 0) { - _pam_forget(new_password); - rad_converse(pamh, PAM_ERROR_MSG, - "You must enter the same password twice.", NULL); - retval = PAM_AUTHTOK_ERR; - goto error; /* ??? maybe this should be a 'continue' ??? */ + + /* + * If there's no client id specified, use the service type, to help + * keep track of which service is doing the authentication. + */ + if (!config.client_id) { + retval = pam_get_item(pamh, PAM_SERVICE, (CONST void **) &config.client_id); + PAM_FAIL_CHECK; } - break; /* everything's fine */ - } /* loop, trying to get matching new passwords */ + /* now we've got a socket open, so we've got to clean it up on error */ +#undef PAM_FAIL_CHECK +#define PAM_FAIL_CHECK if (retval != PAM_SUCCESS) {goto error; } - if (attempts >= 3) { /* too many new password attempts: die */ - retval = PAM_AUTHTOK_ERR; - goto error; - } - } /* now we have a new password which passes all of our tests */ + /* grab the old password (if any) from the previous password layer */ + retval = pam_get_item(pamh, PAM_OLDAUTHTOK, (CONST void **) &password); + PAM_FAIL_CHECK; + if(password) password = strdup(password); - /* - * Solaris 2.6 calls pam_sm_chauthtok only ONCE, with PAM_PRELIM_CHECK - * set. - */ + /* grab the new password (if any) from the previous password layer */ + retval = pam_get_item(pamh, PAM_AUTHTOK, (CONST void **) &new_password); + PAM_FAIL_CHECK; + if(new_password) new_password = strdup(new_password); + + /* preliminary password change checks. */ + if (flags & PAM_PRELIM_CHECK) { + if (!password) { /* no previous password: ask for one */ + retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, "Password: ", &password); + PAM_FAIL_CHECK; + } + + /* + * We now check the password to see if it's the right one. + * If it isn't, we let the user try again. + * Note that RADIUS doesn't have any concept of 'root'. The only way + * that root can change someone's password is to log into the RADIUS + * server, and and change it there. + */ + + /* build and initialize the access request RADIUS packet */ + request->code = PW_AUTHENTICATION_REQUEST; + get_random_vector(request->vector); + request->id = request->vector[0]; /* this should be evenly distributed */ + + build_radius_packet(request, user, password, &config); + add_int_attribute(request, PW_USER_SERVICE_TYPE, PW_AUTHENTICATE_ONLY); + + retval = talk_radius(&config, request, response, password, NULL, 1); + PAM_FAIL_CHECK; + + /* oops! They don't have the right password. Complain and die. */ + if (response->code != PW_AUTHENTICATION_ACK) { + _pam_forget(password); + retval = PAM_PERM_DENIED; + goto error; + } + + /* + * We're now sure it's the right user. + * Ask for their new password, if appropriate + */ + + if (!new_password) { /* not found yet: ask for it */ + int new_attempts; + attempts = 0; + + /* loop, trying to get matching new passwords */ + while (attempts++ < 3) { + + /* loop, trying to get a new password */ + new_attempts = 0; + while (new_attempts++ < 3) { + retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, + "New password: ", &new_password); + PAM_FAIL_CHECK; + + /* the old password may be short. Check it, first. */ + if (strcmp(password, new_password) == 0) { /* are they the same? */ + rad_converse(pamh, PAM_ERROR_MSG, + "You must choose a new password.", NULL); + _pam_forget(new_password); + continue; + } else if (strlen(new_password) < 6) { + rad_converse(pamh, PAM_ERROR_MSG, "it's WAY too short", NULL); + _pam_forget(new_password); + continue; + } + + /* insert crypt password checking here */ + + break; /* the new password is OK */ + } + + if (new_attempts >= 3) { /* too many new password attempts: die */ + retval = PAM_AUTHTOK_ERR; + goto error; + } + + /* make sure of the password by asking for verification */ + retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, + "New password (again): ", &check_password); + PAM_FAIL_CHECK; + + retval = strcmp(new_password, check_password); + _pam_forget(check_password); + + /* if they don't match, don't pass them to the next module */ + if (retval != 0) { + _pam_forget(new_password); + rad_converse(pamh, PAM_ERROR_MSG, + "You must enter the same password twice.", NULL); + retval = PAM_AUTHTOK_ERR; + goto error; /* ??? maybe this should be a 'continue' ??? */ + } + + break; /* everything's fine */ + } /* loop, trying to get matching new passwords */ + + if (attempts >= 3) { /* too many new password attempts: die */ + retval = PAM_AUTHTOK_ERR; + goto error; + } + } /* now we have a new password which passes all of our tests */ + + /* + * Solaris 2.6 calls pam_sm_chauthtok only ONCE, with PAM_PRELIM_CHECK + * set. + */ #ifndef sun - /* If told to update the authentication token, do so. */ - } else if (flags & PAM_UPDATE_AUTHTOK) { + /* If told to update the authentication token, do so. */ + } else if (flags & PAM_UPDATE_AUTHTOK) { #endif - if (!password || !new_password) { /* ensure we've got passwords */ - retval = PAM_AUTHTOK_ERR; - goto error; - } - - /* build and initialize the password change request RADIUS packet */ - request->code = PW_PASSWORD_REQUEST; - get_random_vector(request->vector); - request->id = request->vector[0]; /* this should be evenly distributed */ - - /* the secret here can not be know to the user, so it's the new password */ - _pam_forget(config.server->secret); - config.server->secret = strdup(password); /* it's free'd later */ - - build_radius_packet(request, user, new_password, &config); - add_password(request, PW_OLD_PASSWORD, password, password); - - retval = talk_radius(&config, request, response, new_password, password, 1); - PAM_FAIL_CHECK; - - /* Whew! Done password changing, check for password acknowledge */ - if (response->code != PW_PASSWORD_ACK) { - retval = PAM_AUTHTOK_ERR; - goto error; - } - } - - /* - * Send the passwords to the next stage if preliminary checks fail, - * or if the password change request fails. - */ - if ((flags & PAM_PRELIM_CHECK) || (retval != PAM_SUCCESS)) { - error: - - /* If there was a password pass it to the next layer */ - if (password && *password) { - pam_set_item(pamh, PAM_OLDAUTHTOK, password); - } - - if (new_password && *new_password) { - pam_set_item(pamh, PAM_AUTHTOK, new_password); - } - } - - if (ctrl & PAM_DEBUG_ARG) { - _pam_log(LOG_DEBUG, "password change %s" - , retval==PAM_SUCCESS ? "succeeded":"failed" ); - } - - close(config.sockfd); - cleanup(config.server); - - _pam_forget(password); - _pam_forget(new_password); - return retval; + if (!password || !new_password) { /* ensure we've got passwords */ + retval = PAM_AUTHTOK_ERR; + goto error; + } + + /* build and initialize the password change request RADIUS packet */ + request->code = PW_PASSWORD_REQUEST; + get_random_vector(request->vector); + request->id = request->vector[0]; /* this should be evenly distributed */ + + /* the secret here can not be know to the user, so it's the new password */ + _pam_forget(config.server->secret); + config.server->secret = strdup(password); /* it's free'd later */ + + build_radius_packet(request, user, new_password, &config); + add_password(request, PW_OLD_PASSWORD, password, password); + + retval = talk_radius(&config, request, response, new_password, password, 1); + PAM_FAIL_CHECK; + + /* Whew! Done password changing, check for password acknowledge */ + if (response->code != PW_PASSWORD_ACK) { + retval = PAM_AUTHTOK_ERR; + goto error; + } + } + + /* + * Send the passwords to the next stage if preliminary checks fail, + * or if the password change request fails. + */ + if ((flags & PAM_PRELIM_CHECK) || (retval != PAM_SUCCESS)) { + error: + + /* If there was a password pass it to the next layer */ + if (password && *password) { + pam_set_item(pamh, PAM_OLDAUTHTOK, password); + } + + if (new_password && *new_password) { + pam_set_item(pamh, PAM_AUTHTOK, new_password); + } + } + + if (ctrl & PAM_DEBUG_ARG) { + _pam_log(LOG_DEBUG, "password change %s", retval==PAM_SUCCESS ? "succeeded" : "failed"); + } + + close(config.sockfd); + cleanup(config.server); + + _pam_forget(password); + _pam_forget(new_password); + return retval; } /* - * Do nothing for account management. This is apparently needed by - * some programs. + * Do nothing for account management. This is apparently needed by + * some programs. */ -PAM_EXTERN int -pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc,CONST char **argv) +PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc,CONST char **argv) { - int retval; - retval = PAM_SUCCESS; - return retval; + int retval; + retval = PAM_SUCCESS; + return retval; } #ifdef PAM_STATIC @@ -1628,13 +1589,13 @@ pam_sm_acct_mgmt(pam_handle_t *pamh,int flags,int argc,CONST char **argv) /* static module data */ struct pam_module _pam_radius_modstruct = { - "pam_radius_auth", - pam_sm_authenticate, - pam_sm_setcred, - pam_sm_acct_mgmt, - pam_sm_open_session, - pam_sm_close_session, - pam_sm_chauthtok, + "pam_radius_auth", + pam_sm_authenticate, + pam_sm_setcred, + pam_sm_acct_mgmt, + pam_sm_open_session, + pam_sm_close_session, + pam_sm_chauthtok, }; #endif |