diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2001-07-08 17:56:34 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2001-07-08 17:56:34 +0000 |
commit | f9144522d7069e785812b3434997fbf2b20713e7 (patch) | |
tree | 1d360bc19b20f8d96b294e6fd82e0ea15ff1425e | |
parent | 9f6b2c42cd06a58a875bb5160fd875e39e6c8998 (diff) |
BSD auth module for radius authentication, from BSDi.
-rw-r--r-- | libexec/Makefile | 4 | ||||
-rw-r--r-- | libexec/login_radius/Makefile | 15 | ||||
-rw-r--r-- | libexec/login_radius/login_radius.8 | 121 | ||||
-rw-r--r-- | libexec/login_radius/login_radius.c | 197 | ||||
-rw-r--r-- | libexec/login_radius/raddauth.c | 576 |
5 files changed, 911 insertions, 2 deletions
diff --git a/libexec/Makefile b/libexec/Makefile index 06f15df1e77..96ab5e741c8 100644 --- a/libexec/Makefile +++ b/libexec/Makefile @@ -1,5 +1,5 @@ # from: @(#)Makefile 5.7 (Berkeley) 4/1/91 -# $OpenBSD: Makefile,v 1.23 2001/06/25 15:51:36 hin Exp $ +# $OpenBSD: Makefile,v 1.24 2001/07/08 17:56:33 millert Exp $ .include <bsd.own.mk> @@ -8,7 +8,7 @@ SUBDIR= atrun comsat fingerd ftpd getNAME getty identd lockspool \ rpc.rquotad rpc.rstatd rpc.rusersd rpc.rwalld rpc.sprayd \ talkd tcpd telnetd tftpd uucpd smtpd SUBDIR+=login_passwd login_skey login_krb4 login_krb4-or-pwd login_reject \ - login_chpass login_lchpass login_token + login_chpass login_lchpass login_token login_radius .if (${YP:L} == "yes") SUBDIR+=rpc.yppasswdd diff --git a/libexec/login_radius/Makefile b/libexec/login_radius/Makefile new file mode 100644 index 00000000000..2d0ded9a9a2 --- /dev/null +++ b/libexec/login_radius/Makefile @@ -0,0 +1,15 @@ +# $OpenBSD: Makefile,v 1.1 2001/07/08 17:56:33 millert Exp $ + +PROG= login_radius +SRCS= login_radius.c raddauth.c +MAN= login_radius.8 +DPADD= ${LIBUTIL} +LDADD= -lutil +CFLAGS+=-Wall + +BINOWN= root +BINGRP= auth +BINMODE=4555 +BINDIR= /usr/libexec/auth + +.include <bsd.prog.mk> diff --git a/libexec/login_radius/login_radius.8 b/libexec/login_radius/login_radius.8 new file mode 100644 index 00000000000..4024d86df74 --- /dev/null +++ b/libexec/login_radius/login_radius.8 @@ -0,0 +1,121 @@ +.\" $OpenBSD: login_radius.8,v 1.1 2001/07/08 17:56:33 millert Exp $ +.\" +.\" Copyright (c) 1996 Berkeley Software Design, Inc. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Berkeley Software Design, +.\" Inc. +.\" 4. The name of Berkeley Software Design, Inc. may not be used to endorse +.\" or promote products derived from this software without specific prior +.\" written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" BSDI $From: login_radius.8,v 1.2 1996/11/11 18:42:02 prb Exp $ +.\" +.Dd August 23, 1996 +.Dt LOGIN_RADIUS 8 +.Os +.Sh NAME +.Nm login_radius +.Nd contact radiusd for authentication +.Sh SYNOPSIS +.Nm login_radius +.Op Fl s Ar service +.Op Fl v Ar name=value +.Ar user +.Op Ar class +.Sh DESCRIPTION +The +.Nm +utility contacts the +.Xr radiusd 8 +daemon to authenticate a user. +When executed as the name +.Pa login_ Ns Ar style +it will request +.Xr radiusd 8 +to use the authentication specified by +.Xr style . +.Pp +Available options are: +.Bl -tag -width indent +.It Fl s +Specify the service. +Currently only +.Li challenge , +.Li login , +and +.Li response +are supported. +.It Fl v +This option and its value are ignored. +.El +.Pp +The +.Nm +utility needs to know a shared secret for each radius server it talks to. +Shared secrets are stored in the file +.Pa /etc/raddb/servers +with the format: +.Bd -literal -offset indent +server shared_secret +.Ed +.Pp +The primary and possible secondary radius servers are defined in the +.Xr login.conf 5 +file by the fields: +.Li radius-server +and +.Li radius-server-alt . +.Pp +It is expected that rather than requesting the radius style directly +(in which case the +.Xr radiusd 8 +server uses a default style) +that +.Nm +will be linked to the various mechanisms desired. +For instance, to have all CRYPTOCard and ActivCard authentication take +place on a remote server via the radius protocol, remove the +.Pa login_activ +and +.Pa login_crypto +modules and link +.Pa login_radius +to both of those names. +Now when the user requests one of those authentication styles, +.Nm +will automatically forward the request to the remote +.Xr radiusd 8 +and request it do the requested style of authentication. +.Pp +Unless the the style being used is listed in the +.Li radius-challenge-styles +entry of the +.Pa /etc/login.conf +file, a password will be requested before sending the request to the +radius server. +.Sh SEE ALSO +.Xr login.conf 5 , +.Xr login 8 , +.Xr radiusd 8 diff --git a/libexec/login_radius/login_radius.c b/libexec/login_radius/login_radius.c new file mode 100644 index 00000000000..ea952597d28 --- /dev/null +++ b/libexec/login_radius/login_radius.c @@ -0,0 +1,197 @@ +/* $OpenBSD: login_radius.c,v 1.1 2001/07/08 17:56:33 millert Exp $ */ + +/*- + * Copyright (c) 1996, 1997 Berkeley Software Design, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Berkeley Software Design, + * Inc. + * 4. The name of Berkeley Software Design, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI $From: login_radius.c,v 1.4 1998/04/14 00:39:02 prb Exp $ + */ +/* + * Copyright(c) 1996 by tfm associates. + * All rights reserved. + * + * tfm associates + * P.O. Box 1244 + * Eugene OR 97440-1244 USA + * + * This contains unpublished proprietary source code of tfm associates. + * The copyright notice above does not evidence any + * actual or intended publication of such source code. + * + * A license is granted to Berkeley Software Design, Inc. by + * tfm associates to modify and/or redistribute this software under the + * terms and conditions of the software License Agreement provided with this + * distribution. The Berkeley Software Design Inc. software License + * Agreement specifies the terms and conditions for redistribution. + */ + +#include <sys/types.h> +#include <netinet/in.h> + +#include <ctype.h> +#include <err.h> +#include <login_cap.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <bsd_auth.h> + +static int cleanstring(char *); +int raddauth(char *, char *, char *, char *, char *, char **); + +int +main(int argc, char **argv) +{ + FILE *back; + char challenge[1024]; + char *class, *service, *style, *username, *password, *emsg; + int c, n; + extern char *__progname; + + back = NULL; + password = class = service = NULL; + + /* + * Usage: login_xxx [-s service] [-v var] user [class] + */ + while ((c = getopt(argc, argv, "ds:v:")) != -1) + switch(c) { + case 'd': + back = stdout; + break; + case 'v': + break; + case 's': /* service */ + service = optarg; + if (strcmp(service, "login") != 0 && + strcmp(service, "challenge") != 0 && + strcmp(service, "response") != 0) + errx(1, "%s not supported", service); + break; + default: + syslog(LOG_ERR, "usage error"); + exit(1); + } + + if (service == NULL) + service = LOGIN_DEFSERVICE; + + switch(argc - optind) { + case 2: + class = argv[optind + 1]; + case 1: + username = argv[optind]; + break; + default: + syslog(LOG_ERR, "usage error"); + exit(1); + } + + if ((style = strrchr(__progname, '/'))) + ++style; + else + style = __progname; + + if (strncmp(style, "login_", 6) == 0) + style += 6; + + if (!cleanstring(style)) + errx(1, "style contains non-printables"); + if (!cleanstring(username)) + errx(1, "username contains non-printables"); + if (service && !cleanstring(service)) + errx(1, "service contains non-printables"); + if (class && !cleanstring(class)) + errx(1, "class contains non-printables"); + + /* + * Filedes 3 is the back channel, where we send back results. + */ + if (back == NULL && (back = fdopen(3, "a")) == NULL) { + syslog(LOG_ERR, "reopening back channel"); + exit(1); + } + + memset(challenge, 0, sizeof(challenge)); + + if (strcmp(service, "response") == 0) { + c = -1; + n = 0; + while (++c < sizeof(challenge) && + read(3, &challenge[c], 1) == 1) { + if (challenge[c] == '\0' && ++n == 2) + break; + if (challenge[c] == '\0' && n == 1) + password = challenge + c + 1; + } + if (n < 2) { + syslog(LOG_ERR, "protocol error on back channel"); + exit(1); + } + } + + emsg = NULL; + + c = raddauth(username, class, style, + strcmp(service, "login") ? challenge : NULL, password, &emsg); + + if (c == 0) { + if (challenge == NULL || *challenge == '\0') + (void)fprintf(back, BI_AUTH "\n"); + else { + (void)fprintf(back, BI_VALUE " challenge %s\n", + auth_mkvalue(challenge)); + (void)fprintf(back, BI_CHALLENGE "\n"); + } + exit(0); + } + if (emsg && strcmp(service, "login") == 0) + (void)fprintf(stderr, "%s\n", emsg); + else if (emsg) + (void)fprintf(back, "value errormsg %s\n", auth_mkvalue(emsg)); + if (strcmp(service, "challenge") == 0) { + (void)fprintf(back, BI_SILENT "\n"); + exit(0); + } + (void)fprintf(back, BI_REJECT "\n"); + exit(1); +} + +static int +cleanstring(char *s) +{ + + while (*s && isgraph(*s) && *s != '\\') + ++s; + return(*s == '\0' ? 1 : 0); +} diff --git a/libexec/login_radius/raddauth.c b/libexec/login_radius/raddauth.c new file mode 100644 index 00000000000..efacabe0952 --- /dev/null +++ b/libexec/login_radius/raddauth.c @@ -0,0 +1,576 @@ +/* $OpenBSD: raddauth.c,v 1.1 2001/07/08 17:56:33 millert Exp $ */ + +/*- + * Copyright (c) 1996, 1997 Berkeley Software Design, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Berkeley Software Design, + * Inc. + * 4. The name of Berkeley Software Design, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI $From: raddauth.c,v 1.6 1998/04/14 00:39:04 prb Exp $ + */ +/* + * Copyright(c) 1996 by tfm associates. + * All rights reserved. + * + * tfm associates + * P.O. Box 1244 + * Eugene OR 97440-1244 USA + * + * This contains unpublished proprietary source code of tfm associates. + * The copyright notice above does not evidence any + * actual or intended publication of such source code. + * + * A license is granted to Berkeley Software Design, Inc. by + * tfm associates to modify and/or redistribute this software under the + * terms and conditions of the software License Agreement provided with this + * distribution. The Berkeley Software Design Inc. software License + * Agreement specifies the terms and conditions for redistribution. + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <login_cap.h> +#include <netdb.h> +#include <setjmp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> +#include <md5.h> + + +#define MAXPWNETNAM 64 /* longest username */ +#define MAXSECRETLEN 128 /* maximum length of secret */ + + +#define AUTH_VECTOR_LEN 16 +#define AUTH_HDR_LEN 20 +#define AUTH_PASS_LEN (256 - 16) +#define PW_AUTHENTICATION_REQUEST 1 +#define PW_AUTHENTICATION_ACK 2 +#define PW_AUTHENTICATION_REJECT 3 +#define PW_ACCESS_CHALLENGE 11 +#define PW_USER_NAME 1 +#define PW_PASSWORD 2 +#define PW_CLIENT_ID 4 +#define PW_CLIENT_PORT_ID 5 +#define PW_PORT_MESSAGE 18 +#define PW_STATE 24 + +#ifndef RADIUS_DIR +#define RADIUS_DIR "/etc/raddb" +#endif +#define RADIUS_SERVERS "servers" + +char *radius_dir = RADIUS_DIR; +char auth_secret[MAXSECRETLEN+1]; +int alt_retries; +int retries; +int sockfd; +int timeout; +jmp_buf timerfail; +in_addr_t alt_server; +in_addr_t auth_server; + +typedef struct { + u_char code; + u_char id; + u_short length; + u_char vector[AUTH_VECTOR_LEN]; + u_char data[4096 - AUTH_HDR_LEN]; +} auth_hdr_t; + +void servtimeout(int); +in_addr_t get_ipaddr(); +in_addr_t gethost(); +int rad_recv(char *, char *); +void parse_challenge(auth_hdr_t *, int, char *, char *); +void rad_request(pid_t, char *, char *, int, char *, char *); +void getsecret(); + +/* + * challenge -- NULL for interactive service + * password -- NULL for interactive service and when requesting a challenge + */ +int +raddauth(char *username, char *class, char *style, char *challenge, + char *password, char **emsg) +{ + volatile pid_t req_id; + char * volatile userstyle, * volatile passwd, * volatile pwstate; + volatile int auth_port; + char vector[AUTH_VECTOR_LEN+1], _pwstate[1024], *p, *v; + int i; + login_cap_t *lc; + u_int32_t r; + struct servent *svp; + struct sockaddr_in sin; + + memset(_pwstate, 0, sizeof(_pwstate)); + pwstate = password ? challenge : _pwstate; + + if ((lc = login_getclass(class)) == NULL) { + snprintf(_pwstate, sizeof(_pwstate), + "%s: no such class", class); + *emsg = _pwstate; + return(1); + } + + timeout = login_getcapnum(lc, "radius-timout", 2, 2); + retries = login_getcapnum(lc, "radius-retries", 6, 6); + if (timeout < 1) + timeout = 1; + if (retries < 2) + retries = 2; + + if (challenge == NULL) { + passwd = NULL; + v = login_getcapstr(lc, "radius-challenge-styles", + NULL, NULL); + i = strlen(style); + while (v && (p = strstr(v, style)) != NULL) { + if ((p == v || p[-1] == ',') && + (p[i] == ',' || p[i] == '\0')) { + passwd = ""; + break; + } + v = p+1; + } + if (passwd == NULL) + passwd = getpass("Password:"); + } else if (password != NULL) + passwd = password; + else + passwd = ""; + + if ((v = login_getcapstr(lc, "radius-server", NULL, NULL)) == NULL){ + *emsg = "radius-server not configured"; + return (1); + } + + auth_server = get_ipaddr(v); + + if ((v = login_getcapstr(lc, "radius-server-alt", NULL, NULL)) == NULL) + alt_server = 0; + else { + alt_server = get_ipaddr(v); + alt_retries = retries/2; + retries >>= 1; + } + + /* get port number */ + svp = getservbyname ("radius", "udp"); + if (svp == NULL) { + *emsg = "No such service: radius/udp"; + return (1); + } + + /* get the secret from the servers file */ + getsecret(); + + /* set up socket */ + if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + snprintf(_pwstate, sizeof(_pwstate), "%s", strerror(errno)); + *emsg = _pwstate; + return(1); + } + + /* set up client structure */ + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = svp->s_port; + + req_id = getpid(); + auth_port = ttyslot(); + if (auth_port == 0) + auth_port = (int)getppid(); + if (strcmp(style, "radius") != 0) { + userstyle = malloc(strlen(username) + strlen(style) + 2); + if (userstyle == NULL) + err(1, NULL); + sprintf(userstyle, "%s:%s", username, style); + } else + userstyle = username; + + /* generate random vector */ + for (i = 0; i < AUTH_VECTOR_LEN; i++) { + r = arc4random(); + vector[i] = r & 0xff; + vector[++i] = (r >> 8) & 0xff; + vector[++i] = (r >> 16) & 0xff; + vector[++i] = (r >> 24) & 0xff; + } + vector[AUTH_VECTOR_LEN] = '\0'; + + signal(SIGALRM, servtimeout); + setjmp(timerfail); + + while (retries > 0) { + rad_request(req_id, userstyle, passwd, auth_port, vector, + pwstate); + + switch (i = rad_recv(_pwstate, challenge)) { + case PW_AUTHENTICATION_ACK: + /* + * Make sure we don't think a challenge was issued. + */ + if (challenge) + *challenge = '\0'; + return (0); + + case PW_AUTHENTICATION_REJECT: + return (1); + + case PW_ACCESS_CHALLENGE: + /* + * If this is a response then reject them if + * we got a challenge. + */ + if (password) + return (1); + /* + * If we wanted a challenge, just return + */ + if (challenge) { + if (strcmp(challenge, _pwstate) != 0) + syslog(LOG_WARNING, + "challenge for %s does not match state", + userstyle); + return (0); + } + req_id++; + passwd = getpass(""); + break; + + default: + snprintf(_pwstate, sizeof(_pwstate), + "invalid response type %d\n", i); + *emsg = _pwstate; + return(1); + } + } + return (1); +} + + +/* + * + * rad_request() -- build a radius authentication digest and + * submit it to the radius server + * + */ +void +rad_request(pid_t id, char *name, char *password, int port, char *vector, + char *state) +{ + auth_hdr_t auth; + int i, len, secretlen, total_length, p; + struct servent *rad_port; + struct sockaddr_in sin; + u_char md5buf[MAXSECRETLEN+AUTH_VECTOR_LEN], digest[AUTH_VECTOR_LEN], + pass_buf[AUTH_PASS_LEN], *pw, *ptr; + u_int length; + in_addr_t ipaddr; + MD5_CTX context; + + memset(&auth, 0, sizeof(auth)); + auth.code = PW_AUTHENTICATION_REQUEST; + auth.id = id; + memcpy(auth.vector, vector, AUTH_VECTOR_LEN); + total_length = AUTH_HDR_LEN; + ptr = auth.data; + + *ptr++ = PW_USER_NAME; + length = strlen(name); + if (length > MAXPWNETNAM) + length = MAXPWNETNAM; + + *ptr++ = length + 2; + memcpy(ptr, name, length); + ptr += length; + total_length += length + 2; + + /* password */ + + /* encrypt the password */ + + length = strlen(password); + if (length > AUTH_PASS_LEN) + length = AUTH_PASS_LEN; + + p = (length + AUTH_VECTOR_LEN - 1) / AUTH_VECTOR_LEN; + *ptr++ = PW_PASSWORD; + *ptr++ = p * AUTH_VECTOR_LEN + 2; + + strncpy(pass_buf, password, AUTH_PASS_LEN); /* must zero fill */ + + /* calculate the md5 digest */ + secretlen = strlen(auth_secret); + memcpy(md5buf, auth_secret, secretlen); + memcpy(md5buf + secretlen, auth.vector, AUTH_VECTOR_LEN); + + total_length += 2; + + /* xor the password into the md5 digest */ + pw = pass_buf; + while (p-- > 0) { + MD5Init(&context); + MD5Update(&context, md5buf, secretlen + AUTH_VECTOR_LEN); + MD5Final(digest, &context); + for (i = 0; i < AUTH_VECTOR_LEN; ++i) { + *ptr = digest[i] ^ *pw; + md5buf[secretlen+i] = *ptr++; + *pw++ = '\0'; + } + total_length += AUTH_VECTOR_LEN; + } + + + /* client id */ + *ptr++ = PW_CLIENT_ID; + *ptr++ = sizeof(in_addr_t) + 2; + ipaddr = gethost(); + memcpy(ptr, &ipaddr, sizeof(in_addr_t)); + ptr += sizeof(in_addr_t); + total_length += sizeof(in_addr_t) + 2; + + /* client port */ + *ptr++ = PW_CLIENT_PORT_ID; + *ptr++ = sizeof(in_addr_t) + 2; + port = htonl(port); + memcpy(ptr, &port, sizeof(int)); + ptr += sizeof(int); + total_length += sizeof(int) + 2; + + /* Append the state info */ + if ((state != NULL) && (strlen(state) > 0)) { + len = strlen(state); + *ptr++ = PW_STATE; + *ptr++ = len + 2; + memcpy(ptr, state, len); + ptr += len; + total_length += len + 2; + } + + auth.length = htons(total_length); + + /* get radius port number */ + rad_port = getservbyname("radius", "udp"); + if (rad_port == NULL) + errx(1, "no such service: radius/udp"); + + memset(&sin, 0, sizeof (sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = auth_server; + sin.sin_port = rad_port->s_port; + if (sendto(sockfd, &auth, total_length, 0, (struct sockaddr *)&sin, + sizeof(sin)) == -1) + err(1, NULL); +} + +/* + * + * rad_recv() -- receive udp responses from the radius server + * + */ +int +rad_recv(char *state, char *challenge) +{ + auth_hdr_t auth; + int nread, salen; + struct sockaddr_in sin; + + salen = sizeof(sin); + + alarm(timeout); + + nread = recvfrom(sockfd, &auth, sizeof(auth), 0, + (struct sockaddr *)&sin, &salen); + alarm(0); + + if (sin.sin_addr.s_addr != auth_server) + errx(1, "bogus authentication server"); + + if (auth.code == PW_ACCESS_CHALLENGE) + parse_challenge(&auth, nread, state, challenge); + + return(auth.code); +} + + +/* + * gethost() -- get local hostname + */ +in_addr_t +gethost() +{ + char hostname[MAXHOSTNAMELEN]; + + if (gethostname(hostname, sizeof(hostname))) + err(1, "gethost"); + return(get_ipaddr(hostname)); +} + +/* + * get_ipaddr -- get an ip address in host long notation from a + * hostname or dotted quad. + */ +in_addr_t +get_ipaddr(char *host) +{ + struct hostent *hp; + + if ((hp = gethostbyname(host)) == NULL) + return(0); + + return (((struct in_addr *)hp->h_addr)->s_addr); +} + +/* + * get the secret from the servers file + */ +void +getsecret() +{ + FILE *servfd; + char *host, *secret, buffer[MAXPATHLEN]; + size_t len; + + snprintf(buffer, sizeof(buffer), "%s/%s", + radius_dir, RADIUS_SERVERS); + + if ((servfd = fopen(buffer, "r")) == NULL) { + syslog(LOG_ERR, "%s: %m", buffer); + return; + } + + secret = NULL; /* Keeps gcc happy */ + + while ((host = fgetln(servfd, &len)) != NULL) { + if (*host == '#') { + memset(host, 0, len); + continue; + } + /* XXX - in no newline we could oflow */ + if (host[len-1] == '\n') + --len; + while (len > 0 && isspace(host[--len])) + ; + host[len+1] = '\0'; + while (isspace(*host)) { + ++host; + --len; + } + if (*host == '\0') + continue; + secret = host; + while (*secret && !isspace(*secret)) + ++secret; + if (*secret) + *secret++ = '\0'; + if (get_ipaddr(host) != auth_server) { + memset(host, 0, len); + continue; + } + while (isspace(*secret)) + ++secret; + if (*secret) + break; + } + if (host) { + strlcpy(auth_secret, secret, sizeof(auth_secret)); + memset(host, 0, len); + } + fclose(servfd); +} + +void +servtimeout(int signo) +{ + + if (--retries <= 0) { + /* + * If we ran out of tries but there is an alternate + * server, switch to it and try again. + */ + if (alt_retries) { + auth_server = alt_server; + retries = alt_retries; + alt_retries = 0; + getsecret(); + } else + warnx("no response from authentication server"); + } + longjmp(timerfail, 1); +} + +void +parse_challenge(auth_hdr_t *authhdr, int length, char *state, char *challenge) +{ + int attribute, attribute_len; + u_char *ptr; + + ptr = authhdr->data; + length = ntohs(authhdr->length) - AUTH_HDR_LEN; + + *state = 0; + + while (length > 0) { + attribute = *ptr++; + attribute_len = *ptr++; + length -= attribute_len; + attribute_len -= 2; + + switch (attribute) { + case PW_PORT_MESSAGE: + if (challenge) { + memcpy(challenge, ptr, attribute_len); + challenge[attribute_len] = '\0'; + } else + printf("%.*s", attribute_len, ptr); + break; + case PW_STATE: + memcpy(state, ptr, attribute_len); + state[attribute_len] = '\0'; + break; + } + ptr += attribute_len; + } +} |