1*04dc6799Schristos /* $NetBSD: radlib.c,v 1.12 2018/02/05 00:43:06 christos Exp $ */
288095537Smanu
388095537Smanu /*-
488095537Smanu * Copyright 1998 Juniper Networks, Inc.
588095537Smanu * All rights reserved.
688095537Smanu *
788095537Smanu * Redistribution and use in source and binary forms, with or without
888095537Smanu * modification, are permitted provided that the following conditions
988095537Smanu * are met:
1088095537Smanu * 1. Redistributions of source code must retain the above copyright
1188095537Smanu * notice, this list of conditions and the following disclaimer.
1288095537Smanu * 2. Redistributions in binary form must reproduce the above copyright
1388095537Smanu * notice, this list of conditions and the following disclaimer in the
1488095537Smanu * documentation and/or other materials provided with the distribution.
1588095537Smanu *
1688095537Smanu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1788095537Smanu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1888095537Smanu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1988095537Smanu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2088095537Smanu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2188095537Smanu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2288095537Smanu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2388095537Smanu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2488095537Smanu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2588095537Smanu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2688095537Smanu * SUCH DAMAGE.
2788095537Smanu */
2888095537Smanu
2988095537Smanu #include <sys/cdefs.h>
3088095537Smanu #ifdef __FreeBSD__
3188095537Smanu __FBSDID("$FreeBSD: /repoman/r/ncvs/src/lib/libradius/radlib.c,v 1.12 2004/06/14 20:55:30 stefanf Exp $");
3288095537Smanu #else
33*04dc6799Schristos __RCSID("$NetBSD: radlib.c,v 1.12 2018/02/05 00:43:06 christos Exp $");
3488095537Smanu #endif
3588095537Smanu
3688095537Smanu #include <sys/types.h>
3788095537Smanu #include <sys/socket.h>
3888095537Smanu #include <sys/time.h>
3988095537Smanu #include <netinet/in.h>
4088095537Smanu #include <arpa/inet.h>
4188095537Smanu #ifdef WITH_SSL
4288095537Smanu #include <openssl/hmac.h>
4388095537Smanu #include <openssl/md5.h>
4488095537Smanu #define MD5Init MD5_Init
4588095537Smanu #define MD5Update MD5_Update
4688095537Smanu #define MD5Final MD5_Final
47c1cfec65Schristos #define MD5Len size_t
4899ab3bdfSchristos #define MD5Buf const void *
4988095537Smanu #else
5088095537Smanu #define MD5_DIGEST_LENGTH 16
51476ca6e1Schristos #define MD5Len unsigned int
5299ab3bdfSchristos #define MD5Buf const unsigned char *
5388095537Smanu #include <md5.h>
5488095537Smanu #endif
5588095537Smanu
5688095537Smanu /* We need the MPPE_KEY_LEN define */
5788095537Smanu #ifdef __FreeBSD__
5888095537Smanu #include <netgraph/ng_mppc.h>
5988095537Smanu #else
6088095537Smanu #define MPPE_KEY_LEN 16
6188095537Smanu #endif
6288095537Smanu
6388095537Smanu #include <errno.h>
6488095537Smanu #include <netdb.h>
6588095537Smanu #include <stdarg.h>
6688095537Smanu #include <stddef.h>
6788095537Smanu #include <stdio.h>
6888095537Smanu #include <stdlib.h>
6988095537Smanu #include <string.h>
7088095537Smanu #include <unistd.h>
7188095537Smanu
7288095537Smanu #include "radlib_private.h"
7388095537Smanu #if !defined(__printflike)
7488095537Smanu #define __printflike(fmtarg, firstvararg) \
7588095537Smanu __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
7688095537Smanu #endif
7788095537Smanu
7888095537Smanu #ifdef __NetBSD__
7988095537Smanu #define srandomdev(x)
8088095537Smanu #define random arc4random
8188095537Smanu #endif
8288095537Smanu
8388095537Smanu static void clear_password(struct rad_handle *);
8488095537Smanu static void generr(struct rad_handle *, const char *, ...)
8588095537Smanu __printflike(2, 3);
86b5c21fe2Slukem static void insert_scrambled_password(struct rad_handle *, size_t);
87b5c21fe2Slukem static void insert_request_authenticator(struct rad_handle *, size_t);
88b5c21fe2Slukem static void insert_message_authenticator(struct rad_handle *, size_t);
89b5c21fe2Slukem static int is_valid_response(struct rad_handle *, size_t,
9088095537Smanu const struct sockaddr_in *);
9188095537Smanu static int put_password_attr(struct rad_handle *, int,
9288095537Smanu const void *, size_t);
9388095537Smanu static int put_raw_attr(struct rad_handle *, int,
9488095537Smanu const void *, size_t);
95d1a11f39Sjmmv static size_t split(char *, const char *[], size_t, char *, size_t);
9688095537Smanu
9788095537Smanu static void
clear_password(struct rad_handle * h)9888095537Smanu clear_password(struct rad_handle *h)
9988095537Smanu {
10088095537Smanu if (h->pass_len != 0) {
10159578938Schristos (void)memset(h->pass, 0, h->pass_len);
10288095537Smanu h->pass_len = 0;
10388095537Smanu }
10488095537Smanu h->pass_pos = 0;
10588095537Smanu }
10688095537Smanu
10788095537Smanu static void
generr(struct rad_handle * h,const char * format,...)10888095537Smanu generr(struct rad_handle *h, const char *format, ...)
10988095537Smanu {
11088095537Smanu va_list ap;
11188095537Smanu
11288095537Smanu va_start(ap, format);
11359578938Schristos vsnprintf(h->errmsg, (size_t)ERRSIZE, format, ap);
11488095537Smanu va_end(ap);
11588095537Smanu }
11688095537Smanu
11788095537Smanu static void
insert_scrambled_password(struct rad_handle * h,size_t srv)118b5c21fe2Slukem insert_scrambled_password(struct rad_handle *h, size_t srv)
11988095537Smanu {
12088095537Smanu MD5_CTX ctx;
12188095537Smanu unsigned char md5[MD5_DIGEST_LENGTH];
12288095537Smanu const struct rad_server *srvp;
12359578938Schristos size_t padded_len, pos;
12488095537Smanu
12588095537Smanu srvp = &h->servers[srv];
12659578938Schristos padded_len = h->pass_len == 0 ? (size_t)16 : (h->pass_len+15) & ~0xf;
12788095537Smanu
12859578938Schristos (void)memcpy(md5, &h->request[POS_AUTH], (size_t)LEN_AUTH);
12988095537Smanu for (pos = 0; pos < padded_len; pos += 16) {
13088095537Smanu int i;
13188095537Smanu
13288095537Smanu /* Calculate the new scrambler */
13388095537Smanu MD5Init(&ctx);
13499ab3bdfSchristos MD5Update(&ctx, (MD5Buf)srvp->secret,
135476ca6e1Schristos (MD5Len)strlen(srvp->secret));
136476ca6e1Schristos MD5Update(&ctx, md5, (MD5Len)16);
13788095537Smanu MD5Final(md5, &ctx);
13888095537Smanu
13988095537Smanu /*
14088095537Smanu * Mix in the current chunk of the password, and copy
14188095537Smanu * the result into the right place in the request. Also
14288095537Smanu * modify the scrambler in place, since we will use this
14388095537Smanu * in calculating the scrambler for next time.
14488095537Smanu */
14588095537Smanu for (i = 0; i < 16; i++)
14688095537Smanu h->request[h->pass_pos + pos + i] =
14788095537Smanu md5[i] ^= h->pass[pos + i];
14888095537Smanu }
14988095537Smanu }
15088095537Smanu
15188095537Smanu static void
insert_request_authenticator(struct rad_handle * h,size_t srv)152b5c21fe2Slukem insert_request_authenticator(struct rad_handle *h, size_t srv)
15388095537Smanu {
15488095537Smanu MD5_CTX ctx;
15588095537Smanu const struct rad_server *srvp;
15688095537Smanu
15788095537Smanu srvp = &h->servers[srv];
15888095537Smanu
15988095537Smanu /* Create the request authenticator */
16088095537Smanu MD5Init(&ctx);
161476ca6e1Schristos MD5Update(&ctx, &h->request[POS_CODE],
162476ca6e1Schristos (MD5Len)(POS_AUTH - POS_CODE));
163476ca6e1Schristos MD5Update(&ctx, memset(&h->request[POS_AUTH], 0, (size_t)LEN_AUTH),
164476ca6e1Schristos (MD5Len)LEN_AUTH);
165476ca6e1Schristos MD5Update(&ctx, &h->request[POS_ATTRS],
166476ca6e1Schristos (MD5Len)(h->req_len - POS_ATTRS));
16799ab3bdfSchristos MD5Update(&ctx, (MD5Buf)srvp->secret,
168476ca6e1Schristos (MD5Len)strlen(srvp->secret));
16988095537Smanu MD5Final(&h->request[POS_AUTH], &ctx);
17088095537Smanu }
17188095537Smanu
17288095537Smanu static void
17399ab3bdfSchristos /*ARGSUSED*/
insert_message_authenticator(struct rad_handle * h,size_t srv)174b5c21fe2Slukem insert_message_authenticator(struct rad_handle *h, size_t srv)
17588095537Smanu {
17688095537Smanu #ifdef WITH_SSL
17788095537Smanu u_char md[EVP_MAX_MD_SIZE];
17888095537Smanu u_int md_len;
17988095537Smanu const struct rad_server *srvp;
180*04dc6799Schristos HMAC_CTX *ctx;
18188095537Smanu srvp = &h->servers[srv];
18288095537Smanu
18388095537Smanu if (h->authentic_pos != 0) {
184*04dc6799Schristos ctx = HMAC_CTX_new();
185*04dc6799Schristos HMAC_Init_ex(ctx, srvp->secret,
186*04dc6799Schristos (int)strlen(srvp->secret), EVP_md5(), NULL);
187*04dc6799Schristos HMAC_Update(ctx, &h->request[POS_CODE], (size_t)(POS_AUTH - POS_CODE));
188*04dc6799Schristos HMAC_Update(ctx, &h->request[POS_AUTH], (size_t)LEN_AUTH);
189*04dc6799Schristos HMAC_Update(ctx, &h->request[POS_ATTRS],
190c1cfec65Schristos (size_t)(h->req_len - POS_ATTRS));
191*04dc6799Schristos HMAC_Final(ctx, md, &md_len);
192*04dc6799Schristos HMAC_CTX_free(ctx);
19359578938Schristos (void)memcpy(&h->request[h->authentic_pos + 2], md,
19459578938Schristos (size_t)md_len);
19588095537Smanu }
19688095537Smanu #endif
19788095537Smanu }
19888095537Smanu
19988095537Smanu /*
20088095537Smanu * Return true if the current response is valid for a request to the
20188095537Smanu * specified server.
20288095537Smanu */
20388095537Smanu static int
is_valid_response(struct rad_handle * h,size_t srv,const struct sockaddr_in * from)204b5c21fe2Slukem is_valid_response(struct rad_handle *h, size_t srv,
20588095537Smanu const struct sockaddr_in *from)
20688095537Smanu {
20788095537Smanu MD5_CTX ctx;
20888095537Smanu unsigned char md5[MD5_DIGEST_LENGTH];
20988095537Smanu const struct rad_server *srvp;
210b5c21fe2Slukem size_t len;
21188095537Smanu #ifdef WITH_SSL
212*04dc6799Schristos HMAC_CTX *hctx;
21388095537Smanu u_char resp[MSGSIZE], md[EVP_MAX_MD_SIZE];
214b5c21fe2Slukem size_t pos;
215476ca6e1Schristos u_int md_len;
21688095537Smanu #endif
21788095537Smanu
21888095537Smanu srvp = &h->servers[srv];
21988095537Smanu
22088095537Smanu /* Check the source address */
22188095537Smanu if (from->sin_family != srvp->addr.sin_family ||
22288095537Smanu from->sin_addr.s_addr != srvp->addr.sin_addr.s_addr ||
22388095537Smanu from->sin_port != srvp->addr.sin_port)
22488095537Smanu return 0;
22588095537Smanu
22688095537Smanu /* Check the message length */
22788095537Smanu if (h->resp_len < POS_ATTRS)
22888095537Smanu return 0;
22988095537Smanu len = h->response[POS_LENGTH] << 8 | h->response[POS_LENGTH+1];
23088095537Smanu if (len > h->resp_len)
23188095537Smanu return 0;
23288095537Smanu
23388095537Smanu /* Check the response authenticator */
23488095537Smanu MD5Init(&ctx);
235476ca6e1Schristos MD5Update(&ctx, &h->response[POS_CODE],
236476ca6e1Schristos (MD5Len)(POS_AUTH - POS_CODE));
237476ca6e1Schristos MD5Update(&ctx, &h->request[POS_AUTH],
238476ca6e1Schristos (MD5Len)LEN_AUTH);
239476ca6e1Schristos MD5Update(&ctx, &h->response[POS_ATTRS],
240476ca6e1Schristos (MD5Len)(len - POS_ATTRS));
24199ab3bdfSchristos MD5Update(&ctx, (MD5Buf)srvp->secret,
242476ca6e1Schristos (MD5Len)strlen(srvp->secret));
24388095537Smanu MD5Final(md5, &ctx);
24488095537Smanu if (memcmp(&h->response[POS_AUTH], md5, sizeof md5) != 0)
24588095537Smanu return 0;
24688095537Smanu
24788095537Smanu #ifdef WITH_SSL
24888095537Smanu /*
24988095537Smanu * For non accounting responses check the message authenticator,
25088095537Smanu * if any.
25188095537Smanu */
25288095537Smanu if (h->response[POS_CODE] != RAD_ACCOUNTING_RESPONSE) {
25388095537Smanu
25459578938Schristos (void)memcpy(resp, h->response, (size_t)MSGSIZE);
25588095537Smanu pos = POS_ATTRS;
25688095537Smanu
25788095537Smanu /* Search and verify the Message-Authenticator */
25888095537Smanu while (pos < len - 2) {
25988095537Smanu
26088095537Smanu if (h->response[pos] == RAD_MESSAGE_AUTHENTIC) {
26188095537Smanu /* zero fill the Message-Authenticator */
26259578938Schristos (void)memset(&resp[pos + 2], 0,
26359578938Schristos (size_t)MD5_DIGEST_LENGTH);
26488095537Smanu
265*04dc6799Schristos hctx = HMAC_CTX_new();
266*04dc6799Schristos HMAC_Init_ex(hctx, srvp->secret,
267*04dc6799Schristos (int)strlen(srvp->secret), EVP_md5(), NULL);
268*04dc6799Schristos HMAC_Update(hctx, &h->response[POS_CODE],
26985fdc9d1Schristos (size_t)(POS_AUTH - POS_CODE));
270*04dc6799Schristos HMAC_Update(hctx, &h->request[POS_AUTH],
27185fdc9d1Schristos (size_t)LEN_AUTH);
272*04dc6799Schristos HMAC_Update(hctx, &resp[POS_ATTRS],
273c1cfec65Schristos (size_t)(h->resp_len - POS_ATTRS));
274*04dc6799Schristos HMAC_Final(hctx, md, &md_len);
275*04dc6799Schristos HMAC_CTX_free(hctx);
27688095537Smanu if (memcmp(md, &h->response[pos + 2],
27759578938Schristos (size_t)MD5_DIGEST_LENGTH) != 0)
27888095537Smanu return 0;
27988095537Smanu break;
28088095537Smanu }
28188095537Smanu pos += h->response[pos + 1];
28288095537Smanu }
28388095537Smanu }
28488095537Smanu #endif
28588095537Smanu return 1;
28688095537Smanu }
28788095537Smanu
28888095537Smanu static int
put_password_attr(struct rad_handle * h,int type,const void * value,size_t len)28988095537Smanu put_password_attr(struct rad_handle *h, int type, const void *value, size_t len)
29088095537Smanu {
291476ca6e1Schristos size_t padded_len;
292476ca6e1Schristos size_t pad_len;
29388095537Smanu
29488095537Smanu if (h->pass_pos != 0) {
29588095537Smanu generr(h, "Multiple User-Password attributes specified");
29688095537Smanu return -1;
29788095537Smanu }
29888095537Smanu if (len > PASSSIZE)
29988095537Smanu len = PASSSIZE;
30088095537Smanu padded_len = len == 0 ? 16 : (len + 15) & ~0xf;
30188095537Smanu pad_len = padded_len - len;
30288095537Smanu
30388095537Smanu /*
30488095537Smanu * Put in a place-holder attribute containing all zeros, and
30588095537Smanu * remember where it is so we can fill it in later.
30688095537Smanu */
30788095537Smanu clear_password(h);
30888095537Smanu put_raw_attr(h, type, h->pass, padded_len);
309738be40cShe h->pass_pos = (int)(h->req_len - padded_len);
31088095537Smanu
31188095537Smanu /* Save the cleartext password, padded as necessary */
312476ca6e1Schristos (void)memcpy(h->pass, value, len);
31388095537Smanu h->pass_len = len;
314476ca6e1Schristos (void)memset(h->pass + len, 0, pad_len);
31588095537Smanu return 0;
31688095537Smanu }
31788095537Smanu
31888095537Smanu static int
put_raw_attr(struct rad_handle * h,int type,const void * value,size_t len)31988095537Smanu put_raw_attr(struct rad_handle *h, int type, const void *value, size_t len)
32088095537Smanu {
32188095537Smanu if (len > 253) {
32288095537Smanu generr(h, "Attribute too long");
32388095537Smanu return -1;
32488095537Smanu }
32588095537Smanu if (h->req_len + 2 + len > MSGSIZE) {
32688095537Smanu generr(h, "Maximum message length exceeded");
32788095537Smanu return -1;
32888095537Smanu }
32988095537Smanu h->request[h->req_len++] = type;
33059578938Schristos h->request[h->req_len++] = (unsigned char)(len + 2);
331476ca6e1Schristos (void)memcpy(&h->request[h->req_len], value, len);
33288095537Smanu h->req_len += len;
33388095537Smanu return 0;
33488095537Smanu }
33588095537Smanu
33688095537Smanu int
rad_add_server(struct rad_handle * h,const char * host,int port,const char * secret,int timeout,int tries)33788095537Smanu rad_add_server(struct rad_handle *h, const char *host, int port,
33888095537Smanu const char *secret, int timeout, int tries)
33988095537Smanu {
34088095537Smanu struct rad_server *srvp;
34188095537Smanu
34288095537Smanu if (h->num_servers >= MAXSERVERS) {
34388095537Smanu generr(h, "Too many RADIUS servers specified");
34488095537Smanu return -1;
34588095537Smanu }
34688095537Smanu srvp = &h->servers[h->num_servers];
34788095537Smanu
34859578938Schristos (void)memset(&srvp->addr, 0, sizeof srvp->addr);
34988095537Smanu srvp->addr.sin_len = sizeof srvp->addr;
35088095537Smanu srvp->addr.sin_family = AF_INET;
35188095537Smanu if (!inet_aton(host, &srvp->addr.sin_addr)) {
35288095537Smanu struct hostent *hent;
35388095537Smanu
35488095537Smanu if ((hent = gethostbyname(host)) == NULL) {
35588095537Smanu generr(h, "%s: host not found", host);
35688095537Smanu return -1;
35788095537Smanu }
35859578938Schristos (void)memcpy(&srvp->addr.sin_addr, hent->h_addr,
35988095537Smanu sizeof srvp->addr.sin_addr);
36088095537Smanu }
36188095537Smanu if (port != 0)
36288095537Smanu srvp->addr.sin_port = htons((u_short)port);
36388095537Smanu else {
36488095537Smanu struct servent *sent;
36588095537Smanu
36688095537Smanu if (h->type == RADIUS_AUTH)
36788095537Smanu srvp->addr.sin_port =
36888095537Smanu (sent = getservbyname("radius", "udp")) != NULL ?
36988095537Smanu sent->s_port : htons(RADIUS_PORT);
37088095537Smanu else
37188095537Smanu srvp->addr.sin_port =
37288095537Smanu (sent = getservbyname("radacct", "udp")) != NULL ?
37388095537Smanu sent->s_port : htons(RADACCT_PORT);
37488095537Smanu }
37588095537Smanu if ((srvp->secret = strdup(secret)) == NULL) {
37688095537Smanu generr(h, "Out of memory");
37788095537Smanu return -1;
37888095537Smanu }
37988095537Smanu srvp->timeout = timeout;
38088095537Smanu srvp->max_tries = tries;
38188095537Smanu srvp->num_tries = 0;
38288095537Smanu h->num_servers++;
38388095537Smanu return 0;
38488095537Smanu }
38588095537Smanu
38688095537Smanu void
rad_close(struct rad_handle * h)38788095537Smanu rad_close(struct rad_handle *h)
38888095537Smanu {
389b5c21fe2Slukem size_t srv;
39088095537Smanu
39188095537Smanu if (h->fd != -1)
39288095537Smanu close(h->fd);
39388095537Smanu for (srv = 0; srv < h->num_servers; srv++) {
39459578938Schristos (void)memset(h->servers[srv].secret, 0,
39588095537Smanu strlen(h->servers[srv].secret));
39688095537Smanu free(h->servers[srv].secret);
39788095537Smanu }
39888095537Smanu clear_password(h);
39988095537Smanu free(h);
40088095537Smanu }
40188095537Smanu
40288095537Smanu int
rad_config(struct rad_handle * h,const char * path)40388095537Smanu rad_config(struct rad_handle *h, const char *path)
40488095537Smanu {
40588095537Smanu FILE *fp;
40688095537Smanu char buf[MAXCONFLINE];
40788095537Smanu int linenum;
40888095537Smanu int retval;
40988095537Smanu
41088095537Smanu if (path == NULL)
41188095537Smanu path = PATH_RADIUS_CONF;
41288095537Smanu if ((fp = fopen(path, "r")) == NULL) {
41388095537Smanu generr(h, "Cannot open \"%s\": %s", path, strerror(errno));
41488095537Smanu return -1;
41588095537Smanu }
41688095537Smanu retval = 0;
41788095537Smanu linenum = 0;
41859578938Schristos while (fgets(buf, (int)sizeof buf, fp) != NULL) {
41959578938Schristos size_t len;
420476ca6e1Schristos const char *fields[5];
421d1a11f39Sjmmv size_t nfields;
42288095537Smanu char msg[ERRSIZE];
423476ca6e1Schristos const char *type;
424476ca6e1Schristos const char *host;
425476ca6e1Schristos char *res;
426476ca6e1Schristos const char *port_str;
427476ca6e1Schristos const char *secret;
428476ca6e1Schristos const char *timeout_str;
429476ca6e1Schristos const char *maxtries_str;
43088095537Smanu char *end;
431476ca6e1Schristos const char *wanttype;
43288095537Smanu unsigned long timeout;
43388095537Smanu unsigned long maxtries;
43488095537Smanu int port;
43559578938Schristos size_t i;
43688095537Smanu
43788095537Smanu linenum++;
43888095537Smanu len = strlen(buf);
43988095537Smanu /* We know len > 0, else fgets would have returned NULL. */
44088095537Smanu if (buf[len - 1] != '\n') {
44188095537Smanu if (len == sizeof buf - 1)
44288095537Smanu generr(h, "%s:%d: line too long", path,
44388095537Smanu linenum);
44488095537Smanu else
44588095537Smanu generr(h, "%s:%d: missing newline", path,
44688095537Smanu linenum);
44788095537Smanu retval = -1;
44888095537Smanu break;
44988095537Smanu }
45088095537Smanu buf[len - 1] = '\0';
45188095537Smanu
45288095537Smanu /* Extract the fields from the line. */
453d1a11f39Sjmmv msg[0] = '\0';
454476ca6e1Schristos nfields = split(buf, fields, sizeof(fields) / sizeof(fields[0]),
455476ca6e1Schristos msg, sizeof msg);
456d1a11f39Sjmmv if (msg[0] != '\0') {
45788095537Smanu generr(h, "%s:%d: %s", path, linenum, msg);
45888095537Smanu retval = -1;
45988095537Smanu break;
46088095537Smanu }
46188095537Smanu if (nfields == 0)
46288095537Smanu continue;
46388095537Smanu /*
46488095537Smanu * The first field should contain "auth" or "acct" for
46588095537Smanu * authentication or accounting, respectively. But older
46688095537Smanu * versions of the file didn't have that field. Default
46788095537Smanu * it to "auth" for backward compatibility.
46888095537Smanu */
46988095537Smanu if (strcmp(fields[0], "auth") != 0 &&
47088095537Smanu strcmp(fields[0], "acct") != 0) {
47188095537Smanu if (nfields >= 5) {
47288095537Smanu generr(h, "%s:%d: invalid service type", path,
47388095537Smanu linenum);
47488095537Smanu retval = -1;
47588095537Smanu break;
47688095537Smanu }
47788095537Smanu nfields++;
47888095537Smanu for (i = nfields; --i > 0; )
47988095537Smanu fields[i] = fields[i - 1];
48088095537Smanu fields[0] = "auth";
48188095537Smanu }
48288095537Smanu if (nfields < 3) {
48388095537Smanu generr(h, "%s:%d: missing shared secret", path,
48488095537Smanu linenum);
48588095537Smanu retval = -1;
48688095537Smanu break;
48788095537Smanu }
48888095537Smanu type = fields[0];
48988095537Smanu host = fields[1];
49088095537Smanu secret = fields[2];
49188095537Smanu timeout_str = fields[3];
49288095537Smanu maxtries_str = fields[4];
49388095537Smanu
49488095537Smanu /* Ignore the line if it is for the wrong service type. */
49588095537Smanu wanttype = h->type == RADIUS_AUTH ? "auth" : "acct";
49688095537Smanu if (strcmp(type, wanttype) != 0)
49788095537Smanu continue;
49888095537Smanu
49988095537Smanu /* Parse and validate the fields. */
500476ca6e1Schristos res = __UNCONST(host);
50188095537Smanu host = strsep(&res, ":");
50288095537Smanu port_str = strsep(&res, ":");
50388095537Smanu if (port_str != NULL) {
50459578938Schristos port = (int)strtoul(port_str, &end, 10);
50588095537Smanu if (*end != '\0') {
50688095537Smanu generr(h, "%s:%d: invalid port", path,
50788095537Smanu linenum);
50888095537Smanu retval = -1;
50988095537Smanu break;
51088095537Smanu }
51188095537Smanu } else
51288095537Smanu port = 0;
51388095537Smanu if (timeout_str != NULL) {
51488095537Smanu timeout = strtoul(timeout_str, &end, 10);
51588095537Smanu if (*end != '\0') {
51688095537Smanu generr(h, "%s:%d: invalid timeout", path,
51788095537Smanu linenum);
51888095537Smanu retval = -1;
51988095537Smanu break;
52088095537Smanu }
52188095537Smanu } else
52288095537Smanu timeout = TIMEOUT;
52388095537Smanu if (maxtries_str != NULL) {
52488095537Smanu maxtries = strtoul(maxtries_str, &end, 10);
52588095537Smanu if (*end != '\0') {
52688095537Smanu generr(h, "%s:%d: invalid maxtries", path,
52788095537Smanu linenum);
52888095537Smanu retval = -1;
52988095537Smanu break;
53088095537Smanu }
53188095537Smanu } else
53288095537Smanu maxtries = MAXTRIES;
53388095537Smanu
534476ca6e1Schristos if (rad_add_server(h, host, port, secret, (int)timeout,
535476ca6e1Schristos (int)maxtries) == -1) {
536476ca6e1Schristos (void)strcpy(msg, h->errmsg);
53788095537Smanu generr(h, "%s:%d: %s", path, linenum, msg);
53888095537Smanu retval = -1;
53988095537Smanu break;
54088095537Smanu }
54188095537Smanu }
54288095537Smanu /* Clear out the buffer to wipe a possible copy of a shared secret */
54359578938Schristos (void)memset(buf, 0, sizeof buf);
54488095537Smanu fclose(fp);
54588095537Smanu return retval;
54688095537Smanu }
54788095537Smanu
54888095537Smanu /*
54988095537Smanu * rad_init_send_request() must have previously been called.
55088095537Smanu * Returns:
55188095537Smanu * 0 The application should select on *fd with a timeout of tv before
55288095537Smanu * calling rad_continue_send_request again.
55388095537Smanu * < 0 Failure
55488095537Smanu * > 0 Success
55588095537Smanu */
55688095537Smanu int
rad_continue_send_request(struct rad_handle * h,int selected,int * fd,struct timeval * tv)55788095537Smanu rad_continue_send_request(struct rad_handle *h, int selected, int *fd,
55888095537Smanu struct timeval *tv)
55988095537Smanu {
56059578938Schristos ssize_t n;
56188095537Smanu
56288095537Smanu if (selected) {
56388095537Smanu struct sockaddr_in from;
564476ca6e1Schristos socklen_t fromlen;
565476ca6e1Schristos ssize_t rv;
56688095537Smanu
56788095537Smanu fromlen = sizeof from;
56859578938Schristos rv = recvfrom(h->fd, h->response, (size_t)MSGSIZE,
56959578938Schristos MSG_WAITALL, (struct sockaddr *)(void *)&from, &fromlen);
570476ca6e1Schristos if (rv == -1) {
57188095537Smanu generr(h, "recvfrom: %s", strerror(errno));
57288095537Smanu return -1;
57388095537Smanu }
574476ca6e1Schristos h->resp_len = rv;
57588095537Smanu if (is_valid_response(h, h->srv, &from)) {
57688095537Smanu h->resp_len = h->response[POS_LENGTH] << 8 |
57788095537Smanu h->response[POS_LENGTH+1];
57888095537Smanu h->resp_pos = POS_ATTRS;
57988095537Smanu return h->response[POS_CODE];
58088095537Smanu }
58188095537Smanu }
58288095537Smanu
58388095537Smanu if (h->try == h->total_tries) {
58488095537Smanu generr(h, "No valid RADIUS responses received");
58588095537Smanu return -1;
58688095537Smanu }
58788095537Smanu
58888095537Smanu /*
58988095537Smanu * Scan round-robin to the next server that has some
59088095537Smanu * tries left. There is guaranteed to be one, or we
59188095537Smanu * would have exited this loop by now.
59288095537Smanu */
59388095537Smanu while (h->servers[h->srv].num_tries >= h->servers[h->srv].max_tries)
59488095537Smanu if (++h->srv >= h->num_servers)
59588095537Smanu h->srv = 0;
59688095537Smanu
59788095537Smanu if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST)
59888095537Smanu /* Insert the request authenticator into the request */
59988095537Smanu insert_request_authenticator(h, h->srv);
60088095537Smanu else
60188095537Smanu /* Insert the scrambled password into the request */
60288095537Smanu if (h->pass_pos != 0)
60388095537Smanu insert_scrambled_password(h, h->srv);
60488095537Smanu
60588095537Smanu insert_message_authenticator(h, h->srv);
60688095537Smanu
60788095537Smanu /* Send the request */
60888095537Smanu n = sendto(h->fd, h->request, h->req_len, 0,
609476ca6e1Schristos (const struct sockaddr *)(void *)&h->servers[h->srv].addr,
610476ca6e1Schristos (socklen_t)sizeof h->servers[h->srv].addr);
61159578938Schristos if (n != (ssize_t)h->req_len) {
61288095537Smanu if (n == -1)
61388095537Smanu generr(h, "sendto: %s", strerror(errno));
61488095537Smanu else
61588095537Smanu generr(h, "sendto: short write");
61688095537Smanu return -1;
61788095537Smanu }
61888095537Smanu
61988095537Smanu h->try++;
62088095537Smanu h->servers[h->srv].num_tries++;
62188095537Smanu tv->tv_sec = h->servers[h->srv].timeout;
62288095537Smanu tv->tv_usec = 0;
62388095537Smanu *fd = h->fd;
62488095537Smanu
62588095537Smanu return 0;
62688095537Smanu }
62788095537Smanu
62888095537Smanu int
rad_create_request(struct rad_handle * h,int code)62988095537Smanu rad_create_request(struct rad_handle *h, int code)
63088095537Smanu {
63188095537Smanu int i;
63288095537Smanu
63388095537Smanu h->request[POS_CODE] = code;
63488095537Smanu h->request[POS_IDENT] = ++h->ident;
63588095537Smanu /* Create a random authenticator */
63688095537Smanu for (i = 0; i < LEN_AUTH; i += 2) {
637476ca6e1Schristos uint32_t r;
638476ca6e1Schristos r = (uint32_t)random();
63988095537Smanu h->request[POS_AUTH+i] = (u_char)r;
64088095537Smanu h->request[POS_AUTH+i+1] = (u_char)(r >> 8);
64188095537Smanu }
64288095537Smanu h->req_len = POS_ATTRS;
64388095537Smanu clear_password(h);
64488095537Smanu h->request_created = 1;
64588095537Smanu return 0;
64688095537Smanu }
64788095537Smanu
64888095537Smanu struct in_addr
rad_cvt_addr(const void * data)64988095537Smanu rad_cvt_addr(const void *data)
65088095537Smanu {
65188095537Smanu struct in_addr value;
65288095537Smanu
65359578938Schristos (void)memcpy(&value.s_addr, data, sizeof value.s_addr);
65488095537Smanu return value;
65588095537Smanu }
65688095537Smanu
65788095537Smanu u_int32_t
rad_cvt_int(const void * data)65888095537Smanu rad_cvt_int(const void *data)
65988095537Smanu {
66088095537Smanu u_int32_t value;
66188095537Smanu
66259578938Schristos (void)memcpy(&value, data, sizeof value);
66388095537Smanu return ntohl(value);
66488095537Smanu }
66588095537Smanu
66688095537Smanu char *
rad_cvt_string(const void * data,size_t len)66788095537Smanu rad_cvt_string(const void *data, size_t len)
66888095537Smanu {
66988095537Smanu char *s;
67088095537Smanu
67188095537Smanu s = malloc(len + 1);
67288095537Smanu if (s != NULL) {
67359578938Schristos (void)memcpy(s, data, len);
67488095537Smanu s[len] = '\0';
67588095537Smanu }
67688095537Smanu return s;
67788095537Smanu }
67888095537Smanu
67988095537Smanu /*
68088095537Smanu * Returns the attribute type. If none are left, returns 0. On failure,
68188095537Smanu * returns -1.
68288095537Smanu */
68388095537Smanu int
rad_get_attr(struct rad_handle * h,const void ** value,size_t * len)68488095537Smanu rad_get_attr(struct rad_handle *h, const void **value, size_t *len)
68588095537Smanu {
68688095537Smanu int type;
68788095537Smanu
68888095537Smanu if (h->resp_pos >= h->resp_len)
68988095537Smanu return 0;
69088095537Smanu if (h->resp_pos + 2 > h->resp_len) {
69188095537Smanu generr(h, "Malformed attribute in response");
69288095537Smanu return -1;
69388095537Smanu }
69488095537Smanu type = h->response[h->resp_pos++];
69588095537Smanu *len = h->response[h->resp_pos++] - 2;
69688095537Smanu if (h->resp_pos + (int)*len > h->resp_len) {
69788095537Smanu generr(h, "Malformed attribute in response");
69888095537Smanu return -1;
69988095537Smanu }
70088095537Smanu *value = &h->response[h->resp_pos];
701738be40cShe h->resp_pos += (int)*len;
70288095537Smanu return type;
70388095537Smanu }
70488095537Smanu
70588095537Smanu /*
70688095537Smanu * Returns -1 on error, 0 to indicate no event and >0 for success
70788095537Smanu */
70888095537Smanu int
rad_init_send_request(struct rad_handle * h,int * fd,struct timeval * tv)70988095537Smanu rad_init_send_request(struct rad_handle *h, int *fd, struct timeval *tv)
71088095537Smanu {
711b5c21fe2Slukem size_t srv;
71288095537Smanu
71388095537Smanu /* Make sure we have a socket to use */
71488095537Smanu if (h->fd == -1) {
71588095537Smanu struct sockaddr_in saddr;
71688095537Smanu
71788095537Smanu if ((h->fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
71888095537Smanu generr(h, "Cannot create socket: %s", strerror(errno));
71988095537Smanu return -1;
72088095537Smanu }
72159578938Schristos (void)memset(&saddr, 0, sizeof saddr);
72288095537Smanu saddr.sin_len = sizeof saddr;
72388095537Smanu saddr.sin_family = AF_INET;
72488095537Smanu saddr.sin_addr.s_addr = INADDR_ANY;
72588095537Smanu saddr.sin_port = htons(0);
726476ca6e1Schristos if (bind(h->fd, (const struct sockaddr *)(void *)&saddr,
72759578938Schristos (socklen_t)sizeof saddr) == -1) {
72888095537Smanu generr(h, "bind: %s", strerror(errno));
72988095537Smanu close(h->fd);
73088095537Smanu h->fd = -1;
73188095537Smanu return -1;
73288095537Smanu }
73388095537Smanu }
73488095537Smanu
73588095537Smanu if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
73688095537Smanu /* Make sure no password given */
73788095537Smanu if (h->pass_pos || h->chap_pass) {
73888095537Smanu generr(h, "User or Chap Password"
73988095537Smanu " in accounting request");
74088095537Smanu return -1;
74188095537Smanu }
74288095537Smanu } else {
74388095537Smanu if (h->eap_msg == 0) {
74488095537Smanu /* Make sure the user gave us a password */
74588095537Smanu if (h->pass_pos == 0 && !h->chap_pass) {
74688095537Smanu generr(h, "No User or Chap Password"
74788095537Smanu " attributes given");
74888095537Smanu return -1;
74988095537Smanu }
75088095537Smanu if (h->pass_pos != 0 && h->chap_pass) {
75188095537Smanu generr(h, "Both User and Chap Password"
75288095537Smanu " attributes given");
75388095537Smanu return -1;
75488095537Smanu }
75588095537Smanu }
75688095537Smanu }
75788095537Smanu
75888095537Smanu /* Fill in the length field in the message */
75959578938Schristos h->request[POS_LENGTH] = (unsigned char)(h->req_len >> 8);
76059578938Schristos h->request[POS_LENGTH+1] = (unsigned char)h->req_len;
76188095537Smanu
76288095537Smanu /*
76388095537Smanu * Count the total number of tries we will make, and zero the
76488095537Smanu * counter for each server.
76588095537Smanu */
76688095537Smanu h->total_tries = 0;
76788095537Smanu for (srv = 0; srv < h->num_servers; srv++) {
76888095537Smanu h->total_tries += h->servers[srv].max_tries;
76988095537Smanu h->servers[srv].num_tries = 0;
77088095537Smanu }
77188095537Smanu if (h->total_tries == 0) {
77288095537Smanu generr(h, "No RADIUS servers specified");
77388095537Smanu return -1;
77488095537Smanu }
77588095537Smanu
77688095537Smanu h->try = h->srv = 0;
77788095537Smanu
77888095537Smanu return rad_continue_send_request(h, 0, fd, tv);
77988095537Smanu }
78088095537Smanu
78188095537Smanu /*
78288095537Smanu * Create and initialize a rad_handle structure, and return it to the
78388095537Smanu * caller. Can fail only if the necessary memory cannot be allocated.
78488095537Smanu * In that case, it returns NULL.
78588095537Smanu */
78688095537Smanu struct rad_handle *
rad_auth_open(void)78788095537Smanu rad_auth_open(void)
78888095537Smanu {
78988095537Smanu struct rad_handle *h;
79088095537Smanu
79188095537Smanu h = (struct rad_handle *)malloc(sizeof(struct rad_handle));
79288095537Smanu if (h != NULL) {
793733ba3e3She srandomdev(0);
79488095537Smanu h->fd = -1;
79588095537Smanu h->num_servers = 0;
79688095537Smanu h->ident = random();
79788095537Smanu h->errmsg[0] = '\0';
79859578938Schristos (void)memset(h->pass, 0, sizeof h->pass);
79988095537Smanu h->pass_len = 0;
80088095537Smanu h->pass_pos = 0;
80188095537Smanu h->chap_pass = 0;
80288095537Smanu h->authentic_pos = 0;
80388095537Smanu h->type = RADIUS_AUTH;
80488095537Smanu h->request_created = 0;
80588095537Smanu h->eap_msg = 0;
80688095537Smanu }
80788095537Smanu return h;
80888095537Smanu }
80988095537Smanu
81088095537Smanu struct rad_handle *
rad_acct_open(void)81188095537Smanu rad_acct_open(void)
81288095537Smanu {
81388095537Smanu struct rad_handle *h;
81488095537Smanu
81588095537Smanu h = rad_open();
81688095537Smanu if (h != NULL)
81788095537Smanu h->type = RADIUS_ACCT;
81888095537Smanu return h;
81988095537Smanu }
82088095537Smanu
82188095537Smanu struct rad_handle *
rad_open(void)82288095537Smanu rad_open(void)
82388095537Smanu {
82488095537Smanu return rad_auth_open();
82588095537Smanu }
82688095537Smanu
82788095537Smanu int
rad_put_addr(struct rad_handle * h,int type,struct in_addr addr)82888095537Smanu rad_put_addr(struct rad_handle *h, int type, struct in_addr addr)
82988095537Smanu {
83088095537Smanu return rad_put_attr(h, type, &addr.s_addr, sizeof addr.s_addr);
83188095537Smanu }
83288095537Smanu
83388095537Smanu int
rad_put_attr(struct rad_handle * h,int type,const void * value,size_t len)83488095537Smanu rad_put_attr(struct rad_handle *h, int type, const void *value, size_t len)
83588095537Smanu {
83688095537Smanu int result;
83788095537Smanu
83888095537Smanu if (!h->request_created) {
83988095537Smanu generr(h, "Please call rad_create_request()"
84088095537Smanu " before putting attributes");
84188095537Smanu return -1;
84288095537Smanu }
84388095537Smanu
84488095537Smanu if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
84588095537Smanu if (type == RAD_EAP_MESSAGE) {
84688095537Smanu generr(h, "EAP-Message attribute is not valid"
84788095537Smanu " in accounting requests");
84888095537Smanu return -1;
84988095537Smanu }
85088095537Smanu }
85188095537Smanu
85288095537Smanu /*
85388095537Smanu * When proxying EAP Messages, the Message Authenticator
85488095537Smanu * MUST be present; see RFC 3579.
85588095537Smanu */
85688095537Smanu if (type == RAD_EAP_MESSAGE) {
85788095537Smanu if (rad_put_message_authentic(h) == -1)
85888095537Smanu return -1;
85988095537Smanu }
86088095537Smanu
86188095537Smanu if (type == RAD_USER_PASSWORD) {
86288095537Smanu result = put_password_attr(h, type, value, len);
86388095537Smanu } else if (type == RAD_MESSAGE_AUTHENTIC) {
86488095537Smanu result = rad_put_message_authentic(h);
86588095537Smanu } else {
86688095537Smanu result = put_raw_attr(h, type, value, len);
86788095537Smanu if (result == 0) {
86888095537Smanu if (type == RAD_CHAP_PASSWORD)
86988095537Smanu h->chap_pass = 1;
87088095537Smanu else if (type == RAD_EAP_MESSAGE)
87188095537Smanu h->eap_msg = 1;
87288095537Smanu }
87388095537Smanu }
87488095537Smanu
87588095537Smanu return result;
87688095537Smanu }
87788095537Smanu
87888095537Smanu int
rad_put_int(struct rad_handle * h,int type,u_int32_t value)87988095537Smanu rad_put_int(struct rad_handle *h, int type, u_int32_t value)
88088095537Smanu {
88188095537Smanu u_int32_t nvalue;
88288095537Smanu
88388095537Smanu nvalue = htonl(value);
88488095537Smanu return rad_put_attr(h, type, &nvalue, sizeof nvalue);
88588095537Smanu }
88688095537Smanu
88788095537Smanu int
rad_put_string(struct rad_handle * h,int type,const char * str)88888095537Smanu rad_put_string(struct rad_handle *h, int type, const char *str)
88988095537Smanu {
89088095537Smanu return rad_put_attr(h, type, str, strlen(str));
89188095537Smanu }
89288095537Smanu
89388095537Smanu int
rad_put_message_authentic(struct rad_handle * h)89488095537Smanu rad_put_message_authentic(struct rad_handle *h)
89588095537Smanu {
89688095537Smanu #ifdef WITH_SSL
89788095537Smanu u_char md_zero[MD5_DIGEST_LENGTH];
89888095537Smanu
89988095537Smanu if (h->request[POS_CODE] == RAD_ACCOUNTING_REQUEST) {
90088095537Smanu generr(h, "Message-Authenticator is not valid"
90188095537Smanu " in accounting requests");
90288095537Smanu return -1;
90388095537Smanu }
90488095537Smanu
90588095537Smanu if (h->authentic_pos == 0) {
906738be40cShe h->authentic_pos = (int)h->req_len;
90759578938Schristos (void)memset(md_zero, 0, sizeof(md_zero));
90888095537Smanu return (put_raw_attr(h, RAD_MESSAGE_AUTHENTIC, md_zero,
90988095537Smanu sizeof(md_zero)));
91088095537Smanu }
91188095537Smanu return 0;
91288095537Smanu #else
91388095537Smanu generr(h, "Message Authenticator not supported,"
91488095537Smanu " please recompile libradius with SSL support");
91588095537Smanu return -1;
91688095537Smanu #endif
91788095537Smanu }
91888095537Smanu
91988095537Smanu /*
92088095537Smanu * Returns the response type code on success, or -1 on failure.
92188095537Smanu */
92288095537Smanu int
rad_send_request(struct rad_handle * h)92388095537Smanu rad_send_request(struct rad_handle *h)
92488095537Smanu {
92588095537Smanu struct timeval timelimit;
92688095537Smanu struct timeval tv;
92788095537Smanu int fd;
92888095537Smanu int n;
92988095537Smanu
93088095537Smanu n = rad_init_send_request(h, &fd, &tv);
93188095537Smanu
93288095537Smanu if (n != 0)
93388095537Smanu return n;
93488095537Smanu
93588095537Smanu gettimeofday(&timelimit, NULL);
93688095537Smanu timeradd(&tv, &timelimit, &timelimit);
93788095537Smanu
93888095537Smanu for ( ; ; ) {
93988095537Smanu fd_set readfds;
94088095537Smanu
94188095537Smanu FD_ZERO(&readfds);
94288095537Smanu FD_SET(fd, &readfds);
94388095537Smanu
94488095537Smanu n = select(fd + 1, &readfds, NULL, NULL, &tv);
94588095537Smanu
94688095537Smanu if (n == -1) {
94788095537Smanu generr(h, "select: %s", strerror(errno));
94888095537Smanu return -1;
94988095537Smanu }
95088095537Smanu
95188095537Smanu if (!FD_ISSET(fd, &readfds)) {
95288095537Smanu /* Compute a new timeout */
95388095537Smanu gettimeofday(&tv, NULL);
95488095537Smanu timersub(&timelimit, &tv, &tv);
95588095537Smanu if (tv.tv_sec > 0 || (tv.tv_sec == 0 && tv.tv_usec > 0))
95688095537Smanu /* Continue the select */
95788095537Smanu continue;
95888095537Smanu }
95988095537Smanu
96088095537Smanu n = rad_continue_send_request(h, n, &fd, &tv);
96188095537Smanu
96288095537Smanu if (n != 0)
96388095537Smanu return n;
96488095537Smanu
96588095537Smanu gettimeofday(&timelimit, NULL);
96688095537Smanu timeradd(&tv, &timelimit, &timelimit);
96788095537Smanu }
96888095537Smanu }
96988095537Smanu
97088095537Smanu const char *
rad_strerror(struct rad_handle * h)97188095537Smanu rad_strerror(struct rad_handle *h)
97288095537Smanu {
97388095537Smanu return h->errmsg;
97488095537Smanu }
97588095537Smanu
97688095537Smanu /*
97788095537Smanu * Destructively split a string into fields separated by white space.
97888095537Smanu * `#' at the beginning of a field begins a comment that extends to the
97988095537Smanu * end of the string. Fields may be quoted with `"'. Inside quoted
98088095537Smanu * strings, the backslash escapes `\"' and `\\' are honored.
98188095537Smanu *
98288095537Smanu * Pointers to up to the first maxfields fields are stored in the fields
98388095537Smanu * array. Missing fields get NULL pointers.
98488095537Smanu *
98588095537Smanu * The return value is the actual number of fields parsed, and is always
98688095537Smanu * <= maxfields.
98788095537Smanu *
988d1a11f39Sjmmv * On a syntax error, places a message in the msg string, and returns
989d1a11f39Sjmmv * SIZE_MAX.
99088095537Smanu */
991d1a11f39Sjmmv static size_t
split(char * str,const char * fields[],size_t maxfields,char * msg,size_t msglen)99259578938Schristos split(char *str, const char *fields[], size_t maxfields, char *msg,
99359578938Schristos size_t msglen)
99488095537Smanu {
99588095537Smanu char *p;
996b5c21fe2Slukem size_t i;
99788095537Smanu static const char ws[] = " \t";
99888095537Smanu
99988095537Smanu for (i = 0; i < maxfields; i++)
100088095537Smanu fields[i] = NULL;
100188095537Smanu p = str;
100288095537Smanu i = 0;
100388095537Smanu while (*p != '\0') {
100488095537Smanu p += strspn(p, ws);
100588095537Smanu if (*p == '#' || *p == '\0')
100688095537Smanu break;
100788095537Smanu if (i >= maxfields) {
100888095537Smanu snprintf(msg, msglen, "line has too many fields");
1009d1a11f39Sjmmv return SIZE_MAX;
101088095537Smanu }
101188095537Smanu if (*p == '"') {
101288095537Smanu char *dst;
101388095537Smanu
101488095537Smanu dst = ++p;
101588095537Smanu fields[i] = dst;
101688095537Smanu while (*p != '"') {
101788095537Smanu if (*p == '\\') {
101888095537Smanu p++;
101988095537Smanu if (*p != '"' && *p != '\\' &&
102088095537Smanu *p != '\0') {
102188095537Smanu snprintf(msg, msglen,
102288095537Smanu "invalid `\\' escape");
1023d1a11f39Sjmmv return SIZE_MAX;
102488095537Smanu }
102588095537Smanu }
102688095537Smanu if (*p == '\0') {
102788095537Smanu snprintf(msg, msglen,
102888095537Smanu "unterminated quoted string");
1029d1a11f39Sjmmv return SIZE_MAX;
103088095537Smanu }
103188095537Smanu *dst++ = *p++;
103288095537Smanu }
103388095537Smanu *dst = '\0';
103488095537Smanu p++;
103588095537Smanu if (*fields[i] == '\0') {
103688095537Smanu snprintf(msg, msglen,
103788095537Smanu "empty quoted string not permitted");
1038d1a11f39Sjmmv return SIZE_MAX;
103988095537Smanu }
104088095537Smanu if (*p != '\0' && strspn(p, ws) == 0) {
104188095537Smanu snprintf(msg, msglen, "quoted string not"
104288095537Smanu " followed by white space");
1043d1a11f39Sjmmv return SIZE_MAX;
104488095537Smanu }
104588095537Smanu } else {
104688095537Smanu fields[i] = p;
104788095537Smanu p += strcspn(p, ws);
104888095537Smanu if (*p != '\0')
104988095537Smanu *p++ = '\0';
105088095537Smanu }
105188095537Smanu i++;
105288095537Smanu }
105388095537Smanu return i;
105488095537Smanu }
105588095537Smanu
105688095537Smanu int
rad_get_vendor_attr(u_int32_t * vendor,const void ** data,size_t * len)105788095537Smanu rad_get_vendor_attr(u_int32_t *vendor, const void **data, size_t *len)
105888095537Smanu {
1059476ca6e1Schristos const struct vendor_attribute *attr;
106088095537Smanu
1061476ca6e1Schristos attr = (const struct vendor_attribute *)*data;
106288095537Smanu *vendor = ntohl(attr->vendor_value);
106388095537Smanu *data = attr->attrib_data;
106488095537Smanu *len = attr->attrib_len - 2;
106588095537Smanu
106688095537Smanu return (attr->attrib_type);
106788095537Smanu }
106888095537Smanu
106988095537Smanu int
rad_put_vendor_addr(struct rad_handle * h,int vendor,int type,struct in_addr addr)107088095537Smanu rad_put_vendor_addr(struct rad_handle *h, int vendor, int type,
107188095537Smanu struct in_addr addr)
107288095537Smanu {
107388095537Smanu return (rad_put_vendor_attr(h, vendor, type, &addr.s_addr,
107488095537Smanu sizeof addr.s_addr));
107588095537Smanu }
107688095537Smanu
107788095537Smanu int
rad_put_vendor_attr(struct rad_handle * h,int vendor,int type,const void * value,size_t len)107888095537Smanu rad_put_vendor_attr(struct rad_handle *h, int vendor, int type,
107988095537Smanu const void *value, size_t len)
108088095537Smanu {
108188095537Smanu struct vendor_attribute *attr;
108288095537Smanu int res;
108388095537Smanu
108488095537Smanu if (!h->request_created) {
108588095537Smanu generr(h, "Please call rad_create_request()"
108688095537Smanu " before putting attributes");
108788095537Smanu return -1;
108888095537Smanu }
108988095537Smanu
109088095537Smanu if ((attr = malloc(len + 6)) == NULL) {
109188095537Smanu generr(h, "malloc failure (%zu bytes)", len + 6);
109288095537Smanu return -1;
109388095537Smanu }
109488095537Smanu
1095476ca6e1Schristos attr->vendor_value = htonl((uint32_t)vendor);
109688095537Smanu attr->attrib_type = type;
109759578938Schristos attr->attrib_len = (unsigned char)(len + 2);
109859578938Schristos (void)memcpy(attr->attrib_data, value, len);
109988095537Smanu
110088095537Smanu res = put_raw_attr(h, RAD_VENDOR_SPECIFIC, attr, len + 6);
110188095537Smanu free(attr);
110288095537Smanu if (res == 0 && vendor == RAD_VENDOR_MICROSOFT
110388095537Smanu && (type == RAD_MICROSOFT_MS_CHAP_RESPONSE
110488095537Smanu || type == RAD_MICROSOFT_MS_CHAP2_RESPONSE)) {
110588095537Smanu h->chap_pass = 1;
110688095537Smanu }
110788095537Smanu return (res);
110888095537Smanu }
110988095537Smanu
111088095537Smanu int
rad_put_vendor_int(struct rad_handle * h,int vendor,int type,u_int32_t i)111188095537Smanu rad_put_vendor_int(struct rad_handle *h, int vendor, int type, u_int32_t i)
111288095537Smanu {
111388095537Smanu u_int32_t value;
111488095537Smanu
111588095537Smanu value = htonl(i);
111688095537Smanu return (rad_put_vendor_attr(h, vendor, type, &value, sizeof value));
111788095537Smanu }
111888095537Smanu
111988095537Smanu int
rad_put_vendor_string(struct rad_handle * h,int vendor,int type,const char * str)112088095537Smanu rad_put_vendor_string(struct rad_handle *h, int vendor, int type,
112188095537Smanu const char *str)
112288095537Smanu {
112388095537Smanu return (rad_put_vendor_attr(h, vendor, type, str, strlen(str)));
112488095537Smanu }
112588095537Smanu
112688095537Smanu ssize_t
rad_request_authenticator(struct rad_handle * h,char * buf,size_t len)112788095537Smanu rad_request_authenticator(struct rad_handle *h, char *buf, size_t len)
112888095537Smanu {
112988095537Smanu if (len < LEN_AUTH)
113088095537Smanu return (-1);
113159578938Schristos (void)memcpy(buf, h->request + POS_AUTH, (size_t)LEN_AUTH);
113288095537Smanu if (len > LEN_AUTH)
113388095537Smanu buf[LEN_AUTH] = '\0';
113488095537Smanu return (LEN_AUTH);
113588095537Smanu }
113688095537Smanu
113788095537Smanu u_char *
rad_demangle(struct rad_handle * h,const void * mangled,size_t mlen)113888095537Smanu rad_demangle(struct rad_handle *h, const void *mangled, size_t mlen)
113988095537Smanu {
114088095537Smanu char R[LEN_AUTH];
114188095537Smanu const char *S;
114288095537Smanu int i, Ppos;
114388095537Smanu MD5_CTX Context;
1144476ca6e1Schristos u_char b[MD5_DIGEST_LENGTH], *demangled;
1145476ca6e1Schristos const u_char *C;
114688095537Smanu
114788095537Smanu if ((mlen % 16 != 0) || mlen > 128) {
114888095537Smanu generr(h, "Cannot interpret mangled data of length %lu",
114988095537Smanu (u_long)mlen);
115088095537Smanu return NULL;
115188095537Smanu }
115288095537Smanu
1153476ca6e1Schristos C = (const u_char *)mangled;
115488095537Smanu
115588095537Smanu /* We need the shared secret as Salt */
115688095537Smanu S = rad_server_secret(h);
115788095537Smanu
115888095537Smanu /* We need the request authenticator */
115988095537Smanu if (rad_request_authenticator(h, R, sizeof R) != LEN_AUTH) {
116088095537Smanu generr(h, "Cannot obtain the RADIUS request authenticator");
116188095537Smanu return NULL;
116288095537Smanu }
116388095537Smanu
116488095537Smanu demangled = malloc(mlen);
116588095537Smanu if (!demangled)
116688095537Smanu return NULL;
116788095537Smanu
116888095537Smanu MD5Init(&Context);
116999ab3bdfSchristos MD5Update(&Context, (MD5Buf)S, (MD5Len)strlen(S));
117099ab3bdfSchristos MD5Update(&Context, (MD5Buf)R, (MD5Len)LEN_AUTH);
117188095537Smanu MD5Final(b, &Context);
117288095537Smanu Ppos = 0;
117388095537Smanu while (mlen) {
117488095537Smanu
117588095537Smanu mlen -= 16;
117688095537Smanu for (i = 0; i < 16; i++)
117788095537Smanu demangled[Ppos++] = C[i] ^ b[i];
117888095537Smanu
117988095537Smanu if (mlen) {
118088095537Smanu MD5Init(&Context);
118199ab3bdfSchristos MD5Update(&Context, (MD5Buf)S, (MD5Len)strlen(S));
118299ab3bdfSchristos MD5Update(&Context, (MD5Buf)C, (MD5Len)16);
118388095537Smanu MD5Final(b, &Context);
118488095537Smanu }
118588095537Smanu
118688095537Smanu C += 16;
118788095537Smanu }
118888095537Smanu
118988095537Smanu return demangled;
119088095537Smanu }
119188095537Smanu
119288095537Smanu u_char *
rad_demangle_mppe_key(struct rad_handle * h,const void * mangled,size_t mlen,size_t * len)119388095537Smanu rad_demangle_mppe_key(struct rad_handle *h, const void *mangled,
119488095537Smanu size_t mlen, size_t *len)
119588095537Smanu {
119688095537Smanu char R[LEN_AUTH]; /* variable names as per rfc2548 */
119788095537Smanu const char *S;
11986ce0a263Schristos u_char b[MD5_DIGEST_LENGTH], *demangled = NULL;
119988095537Smanu const u_char *A, *C;
120088095537Smanu MD5_CTX Context;
1201476ca6e1Schristos size_t Slen, Clen, i, Ppos;
120288095537Smanu u_char *P;
120388095537Smanu
120488095537Smanu if (mlen % 16 != SALT_LEN) {
120588095537Smanu generr(h, "Cannot interpret mangled data of length %lu",
120688095537Smanu (u_long)mlen);
120788095537Smanu return NULL;
120888095537Smanu }
120988095537Smanu
121088095537Smanu /* We need the RADIUS Request-Authenticator */
121188095537Smanu if (rad_request_authenticator(h, R, sizeof R) != LEN_AUTH) {
121288095537Smanu generr(h, "Cannot obtain the RADIUS request authenticator");
121388095537Smanu return NULL;
121488095537Smanu }
121588095537Smanu
121688095537Smanu A = (const u_char *)mangled; /* Salt comes first */
121788095537Smanu C = (const u_char *)mangled + SALT_LEN; /* Then the ciphertext */
121888095537Smanu Clen = mlen - SALT_LEN;
121988095537Smanu S = rad_server_secret(h); /* We need the RADIUS secret */
122088095537Smanu Slen = strlen(S);
12216ce0a263Schristos P = malloc(Clen); /* We derive our plaintext */
122288095537Smanu
122388095537Smanu MD5Init(&Context);
122499ab3bdfSchristos MD5Update(&Context, (MD5Buf)S, (MD5Len)Slen);
122599ab3bdfSchristos MD5Update(&Context, (MD5Buf)R, (MD5Len)LEN_AUTH);
122699ab3bdfSchristos MD5Update(&Context, (MD5Buf)A, (MD5Len)SALT_LEN);
122788095537Smanu MD5Final(b, &Context);
122888095537Smanu Ppos = 0;
122988095537Smanu
123088095537Smanu while (Clen) {
123188095537Smanu Clen -= 16;
123288095537Smanu
123388095537Smanu for (i = 0; i < 16; i++)
123488095537Smanu P[Ppos++] = C[i] ^ b[i];
123588095537Smanu
123688095537Smanu if (Clen) {
123788095537Smanu MD5Init(&Context);
123899ab3bdfSchristos MD5Update(&Context, (MD5Buf)S, (MD5Len)Slen);
123999ab3bdfSchristos MD5Update(&Context, (MD5Buf)C, (MD5Len)16);
124088095537Smanu MD5Final(b, &Context);
124188095537Smanu }
124288095537Smanu
124388095537Smanu C += 16;
124488095537Smanu }
124588095537Smanu
124688095537Smanu /*
124788095537Smanu * The resulting plain text consists of a one-byte length, the text and
124888095537Smanu * maybe some padding.
124988095537Smanu */
125088095537Smanu *len = *P;
125188095537Smanu if (*len > mlen - 1) {
125288095537Smanu generr(h, "Mangled data seems to be garbage %zu %zu",
125388095537Smanu *len, mlen-1);
12546ce0a263Schristos goto out;
125588095537Smanu }
125688095537Smanu
125788095537Smanu if (*len > MPPE_KEY_LEN * 2) {
125888095537Smanu generr(h, "Key to long (%zu) for me max. %d",
125988095537Smanu *len, MPPE_KEY_LEN * 2);
12606ce0a263Schristos goto out;
126188095537Smanu }
126288095537Smanu demangled = malloc(*len);
126388095537Smanu if (!demangled)
12646ce0a263Schristos goto out;
126588095537Smanu
126659578938Schristos (void)memcpy(demangled, P + 1, *len);
12676ce0a263Schristos out:
12686ce0a263Schristos free(P);
126988095537Smanu return demangled;
127088095537Smanu }
127188095537Smanu
127288095537Smanu const char *
rad_server_secret(struct rad_handle * h)127388095537Smanu rad_server_secret(struct rad_handle *h)
127488095537Smanu {
127588095537Smanu return (h->servers[h->srv].secret);
127688095537Smanu }
1277