1/*	$OpenBSD: radius_userpass.c,v 1.2 2023/07/08 08:53:26 yasuoka Exp $ */
2
3/*-
4 * Copyright (c) 2013 Internet Initiative Japan Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE"AUTHOR" AND CONTRIBUTORS AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <stdio.h>
30#include <string.h>
31
32#include <openssl/md5.h>
33
34#include "radius.h"
35
36#include "radius_local.h"
37
38int
39radius_encrypt_user_password_attr(void *cipher, size_t * clen,
40    const char *plain, const void *ra, const char *secret)
41{
42	size_t		 plen = strlen(plain);
43	size_t		 slen = strlen(secret);
44	char		 b[16], p[16], *c;
45	size_t		 off;
46	MD5_CTX		 ctx;
47	unsigned int	 i;
48
49	if (*clen < ROUNDUP(plen, 16))
50		return (-1);
51
52	for (off = 0; off < plen; off += sizeof(p)) {
53		c = ((char *)cipher) + off;
54		memset(p, 0, sizeof(p));
55		strncpy(p, plain + off, sizeof(p));	/* not strlcpy() */
56		MD5_Init(&ctx);
57		MD5_Update(&ctx, secret, slen);
58		if (off == 0)
59			MD5_Update(&ctx, ra, 16);
60		else
61			MD5_Update(&ctx, c - 16, 16);
62		MD5_Final(b, &ctx);
63		for (i = 0; i < 16; i++)
64			c[i] = p[i] ^ b[i];
65	}
66
67	*clen = off;
68	return (0);
69}
70
71int
72radius_decrypt_user_password_attr(char *plain, size_t plen, const void *cipher,
73    size_t clen, const void *ra, const char *secret)
74{
75	size_t slen = strlen(secret);
76	char b[16];
77	size_t off;
78	char *p, *c;
79	MD5_CTX ctx;
80	unsigned int i;
81
82	if (clen % 16 != 0)
83		return (-1);
84	if (plen < clen + 1)
85		return (-1);
86
87	for (off = 0; off < clen; off += 16) {
88		c = ((char *)cipher) + off;
89		p = plain + off;
90		MD5_Init(&ctx);
91		MD5_Update(&ctx, secret, slen);
92		if (off == 0)
93			MD5_Update(&ctx, ra, 16);
94		else
95			MD5_Update(&ctx, c - 16, 16);
96		MD5_Final(b, &ctx);
97		for (i = 0; i < 16; i++)
98			p[i] = c[i] ^ b[i];
99	}
100
101	p = memchr(plain, '\0', off);
102	if (p == NULL)
103		plain[off] = '\0';
104	else {
105		/* memcspn() does not exist... */
106		for (p++; p < plain + off; p++) {
107			if (*p != '\0')
108				return (-1);
109		}
110	}
111
112	return (0);
113}
114
115int
116radius_get_user_password_attr(const RADIUS_PACKET * packet, char *buf,
117    size_t len, const char *secret)
118{
119	char	 cipher[256];
120	size_t	 clen = sizeof(cipher);
121
122	if (radius_get_raw_attr(packet, RADIUS_TYPE_USER_PASSWORD, cipher,
123	    &clen) != 0)
124		return (-1);
125	if (radius_decrypt_user_password_attr(buf, len, cipher, clen,
126	    radius_get_authenticator_retval(packet), secret) != 0)
127		return (-1);
128
129	return (0);
130}
131
132int
133radius_put_user_password_attr(RADIUS_PACKET * packet, const char *buf,
134    const char *secret)
135{
136	char	 cipher[256];
137	size_t	 clen = sizeof(cipher);
138
139	if (radius_encrypt_user_password_attr(cipher, &clen, buf,
140	    radius_get_authenticator_retval(packet), secret) != 0)
141		return (-1);
142	if (radius_put_raw_attr(packet, RADIUS_TYPE_USER_PASSWORD, cipher,
143	    clen) != 0)
144		return (-1);
145
146	return (0);
147}
148