xref: /openbsd-src/lib/libradius/radius_userpass.c (revision 7426d5d9f11d81a7d781a7568167f4e6deed3928)
1*7426d5d9Syasuoka /*	$OpenBSD: radius_userpass.c,v 1.2 2023/07/08 08:53:26 yasuoka Exp $ */
20eaf192dSyasuoka 
30eaf192dSyasuoka /*-
40eaf192dSyasuoka  * Copyright (c) 2013 Internet Initiative Japan Inc.
50eaf192dSyasuoka  * All rights reserved.
60eaf192dSyasuoka  *
70eaf192dSyasuoka  * Redistribution and use in source and binary forms, with or without
80eaf192dSyasuoka  * modification, are permitted provided that the following conditions
90eaf192dSyasuoka  * are met:
100eaf192dSyasuoka  * 1. Redistributions of source code must retain the above copyright
110eaf192dSyasuoka  *    notice, this list of conditions and the following disclaimer.
120eaf192dSyasuoka  * 2. Redistributions in binary form must reproduce the above copyright
130eaf192dSyasuoka  *    notice, this list of conditions and the following disclaimer in the
140eaf192dSyasuoka  *    documentation and/or other materials provided with the distribution.
150eaf192dSyasuoka  *
160eaf192dSyasuoka  * THIS SOFTWARE IS PROVIDED BY THE"AUTHOR" AND CONTRIBUTORS AS IS'' AND
170eaf192dSyasuoka  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
180eaf192dSyasuoka  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
190eaf192dSyasuoka  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
200eaf192dSyasuoka  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
210eaf192dSyasuoka  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
220eaf192dSyasuoka  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
230eaf192dSyasuoka  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
240eaf192dSyasuoka  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
250eaf192dSyasuoka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
260eaf192dSyasuoka  * SUCH DAMAGE.
270eaf192dSyasuoka  */
280eaf192dSyasuoka 
290eaf192dSyasuoka #include <stdio.h>
300eaf192dSyasuoka #include <string.h>
310eaf192dSyasuoka 
320eaf192dSyasuoka #include <openssl/md5.h>
330eaf192dSyasuoka 
340eaf192dSyasuoka #include "radius.h"
350eaf192dSyasuoka 
360eaf192dSyasuoka #include "radius_local.h"
370eaf192dSyasuoka 
380eaf192dSyasuoka int
radius_encrypt_user_password_attr(void * cipher,size_t * clen,const char * plain,const void * ra,const char * secret)390eaf192dSyasuoka radius_encrypt_user_password_attr(void *cipher, size_t * clen,
400eaf192dSyasuoka     const char *plain, const void *ra, const char *secret)
410eaf192dSyasuoka {
420eaf192dSyasuoka 	size_t		 plen = strlen(plain);
430eaf192dSyasuoka 	size_t		 slen = strlen(secret);
440eaf192dSyasuoka 	char		 b[16], p[16], *c;
450eaf192dSyasuoka 	size_t		 off;
460eaf192dSyasuoka 	MD5_CTX		 ctx;
470eaf192dSyasuoka 	unsigned int	 i;
480eaf192dSyasuoka 
490eaf192dSyasuoka 	if (*clen < ROUNDUP(plen, 16))
500eaf192dSyasuoka 		return (-1);
510eaf192dSyasuoka 
520eaf192dSyasuoka 	for (off = 0; off < plen; off += sizeof(p)) {
530eaf192dSyasuoka 		c = ((char *)cipher) + off;
540eaf192dSyasuoka 		memset(p, 0, sizeof(p));
550eaf192dSyasuoka 		strncpy(p, plain + off, sizeof(p));	/* not strlcpy() */
560eaf192dSyasuoka 		MD5_Init(&ctx);
570eaf192dSyasuoka 		MD5_Update(&ctx, secret, slen);
580eaf192dSyasuoka 		if (off == 0)
590eaf192dSyasuoka 			MD5_Update(&ctx, ra, 16);
600eaf192dSyasuoka 		else
610eaf192dSyasuoka 			MD5_Update(&ctx, c - 16, 16);
620eaf192dSyasuoka 		MD5_Final(b, &ctx);
630eaf192dSyasuoka 		for (i = 0; i < 16; i++)
640eaf192dSyasuoka 			c[i] = p[i] ^ b[i];
650eaf192dSyasuoka 	}
660eaf192dSyasuoka 
670eaf192dSyasuoka 	*clen = off;
680eaf192dSyasuoka 	return (0);
690eaf192dSyasuoka }
700eaf192dSyasuoka 
710eaf192dSyasuoka int
radius_decrypt_user_password_attr(char * plain,size_t plen,const void * cipher,size_t clen,const void * ra,const char * secret)720eaf192dSyasuoka radius_decrypt_user_password_attr(char *plain, size_t plen, const void *cipher,
730eaf192dSyasuoka     size_t clen, const void *ra, const char *secret)
740eaf192dSyasuoka {
750eaf192dSyasuoka 	size_t slen = strlen(secret);
760eaf192dSyasuoka 	char b[16];
770eaf192dSyasuoka 	size_t off;
780eaf192dSyasuoka 	char *p, *c;
790eaf192dSyasuoka 	MD5_CTX ctx;
800eaf192dSyasuoka 	unsigned int i;
810eaf192dSyasuoka 
820eaf192dSyasuoka 	if (clen % 16 != 0)
830eaf192dSyasuoka 		return (-1);
840eaf192dSyasuoka 	if (plen < clen + 1)
850eaf192dSyasuoka 		return (-1);
860eaf192dSyasuoka 
870eaf192dSyasuoka 	for (off = 0; off < clen; off += 16) {
880eaf192dSyasuoka 		c = ((char *)cipher) + off;
890eaf192dSyasuoka 		p = plain + off;
900eaf192dSyasuoka 		MD5_Init(&ctx);
910eaf192dSyasuoka 		MD5_Update(&ctx, secret, slen);
920eaf192dSyasuoka 		if (off == 0)
930eaf192dSyasuoka 			MD5_Update(&ctx, ra, 16);
940eaf192dSyasuoka 		else
950eaf192dSyasuoka 			MD5_Update(&ctx, c - 16, 16);
960eaf192dSyasuoka 		MD5_Final(b, &ctx);
970eaf192dSyasuoka 		for (i = 0; i < 16; i++)
980eaf192dSyasuoka 			p[i] = c[i] ^ b[i];
990eaf192dSyasuoka 	}
1000eaf192dSyasuoka 
1010eaf192dSyasuoka 	p = memchr(plain, '\0', off);
1020eaf192dSyasuoka 	if (p == NULL)
1030eaf192dSyasuoka 		plain[off] = '\0';
1040eaf192dSyasuoka 	else {
1050eaf192dSyasuoka 		/* memcspn() does not exist... */
1060eaf192dSyasuoka 		for (p++; p < plain + off; p++) {
1070eaf192dSyasuoka 			if (*p != '\0')
1080eaf192dSyasuoka 				return (-1);
1090eaf192dSyasuoka 		}
1100eaf192dSyasuoka 	}
1110eaf192dSyasuoka 
1120eaf192dSyasuoka 	return (0);
1130eaf192dSyasuoka }
1140eaf192dSyasuoka 
1150eaf192dSyasuoka int
radius_get_user_password_attr(const RADIUS_PACKET * packet,char * buf,size_t len,const char * secret)1160eaf192dSyasuoka radius_get_user_password_attr(const RADIUS_PACKET * packet, char *buf,
1170eaf192dSyasuoka     size_t len, const char *secret)
1180eaf192dSyasuoka {
1190eaf192dSyasuoka 	char	 cipher[256];
1200eaf192dSyasuoka 	size_t	 clen = sizeof(cipher);
1210eaf192dSyasuoka 
1220eaf192dSyasuoka 	if (radius_get_raw_attr(packet, RADIUS_TYPE_USER_PASSWORD, cipher,
1230eaf192dSyasuoka 	    &clen) != 0)
1240eaf192dSyasuoka 		return (-1);
1250eaf192dSyasuoka 	if (radius_decrypt_user_password_attr(buf, len, cipher, clen,
1260eaf192dSyasuoka 	    radius_get_authenticator_retval(packet), secret) != 0)
1270eaf192dSyasuoka 		return (-1);
1280eaf192dSyasuoka 
1290eaf192dSyasuoka 	return (0);
1300eaf192dSyasuoka }
1310eaf192dSyasuoka 
1320eaf192dSyasuoka int
radius_put_user_password_attr(RADIUS_PACKET * packet,const char * buf,const char * secret)1330eaf192dSyasuoka radius_put_user_password_attr(RADIUS_PACKET * packet, const char *buf,
1340eaf192dSyasuoka     const char *secret)
1350eaf192dSyasuoka {
1360eaf192dSyasuoka 	char	 cipher[256];
1370eaf192dSyasuoka 	size_t	 clen = sizeof(cipher);
1380eaf192dSyasuoka 
1390eaf192dSyasuoka 	if (radius_encrypt_user_password_attr(cipher, &clen, buf,
1400eaf192dSyasuoka 	    radius_get_authenticator_retval(packet), secret) != 0)
1410eaf192dSyasuoka 		return (-1);
1420eaf192dSyasuoka 	if (radius_put_raw_attr(packet, RADIUS_TYPE_USER_PASSWORD, cipher,
1430eaf192dSyasuoka 	    clen) != 0)
1440eaf192dSyasuoka 		return (-1);
1450eaf192dSyasuoka 
1460eaf192dSyasuoka 	return (0);
1470eaf192dSyasuoka }
148