summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>2001-07-08 17:56:34 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>2001-07-08 17:56:34 +0000
commitf9144522d7069e785812b3434997fbf2b20713e7 (patch)
tree1d360bc19b20f8d96b294e6fd82e0ea15ff1425e
parent9f6b2c42cd06a58a875bb5160fd875e39e6c8998 (diff)
BSD auth module for radius authentication, from BSDi.
-rw-r--r--libexec/Makefile4
-rw-r--r--libexec/login_radius/Makefile15
-rw-r--r--libexec/login_radius/login_radius.8121
-rw-r--r--libexec/login_radius/login_radius.c197
-rw-r--r--libexec/login_radius/raddauth.c576
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;
+ }
+}