1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or (at
5  *   your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: d0f3292ff0ec5fac9051ac18da04411abcef90a7 $
19  * @file rlm_chap.c
20  * @brief Process chap authentication requests.
21  *
22  * @copyright 2001,2006  The FreeRADIUS server project
23  * @copyright 2001  Kostas Kalevras <kkalev@noc.ntua.gr>
24  */
25 RCSID("$Id: d0f3292ff0ec5fac9051ac18da04411abcef90a7 $")
26 
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29 
CC_HINT(nonnull)30 static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST *request)
31 {
32 	if (!fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) {
33 		return RLM_MODULE_NOOP;
34 	}
35 
36 	if (fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY) != NULL) {
37 		RWDEBUG2("&control:Auth-Type already set.  Not setting to CHAP");
38 		return RLM_MODULE_NOOP;
39 	}
40 
41 	RINDENT();
42 	RDEBUG("&control:Auth-Type := CHAP");
43 	REXDENT();
44 	pair_make_config("Auth-Type", "CHAP", T_OP_EQ);
45 
46 	return RLM_MODULE_OK;
47 }
48 
49 
50 /*
51  *	Find the named user in this modules database.  Create the set
52  *	of attribute-value pairs to check and reply with for this user
53  *	from the database. The authentication code only needs to check
54  *	the password, the rest is done here.
55  */
CC_HINT(nonnull)56 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQUEST *request)
57 {
58 	VALUE_PAIR *password, *chap;
59 	uint8_t pass_str[MAX_STRING_LEN];
60 
61 	if (!request->username) {
62 		REDEBUG("&request:User-Name attribute is required for authentication");
63 		return RLM_MODULE_INVALID;
64 	}
65 
66 	chap = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
67 	if (!chap) {
68 		REDEBUG("You set '&control:Auth-Type = CHAP' for a request that "
69 			"does not contain a CHAP-Password attribute!");
70 		return RLM_MODULE_INVALID;
71 	}
72 
73 	if (chap->vp_length == 0) {
74 		REDEBUG("&request:CHAP-Password is empty");
75 		return RLM_MODULE_INVALID;
76 	}
77 
78 	if (chap->vp_length != CHAP_VALUE_LENGTH + 1) {
79 		REDEBUG("&request:CHAP-Password has invalid length");
80 		return RLM_MODULE_INVALID;
81 	}
82 
83 	password = fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
84 	if (password == NULL) {
85 		if (fr_pair_find_by_num(request->config, PW_USER_PASSWORD, 0, TAG_ANY) != NULL){
86 			REDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
87 			REDEBUG("!!! Please update your configuration so that the \"known !!!");
88 			REDEBUG("!!! good\" cleartext password is in Cleartext-Password,  !!!");
89 			REDEBUG("!!! and NOT in User-Password.                            !!!");
90 			REDEBUG("!!!						          !!!");
91 			REDEBUG("!!! Authentication will fail because of this.	          !!!");
92 			REDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
93 		}
94 
95 		REDEBUG("&control:Cleartext-Password is required for authentication");
96 		return RLM_MODULE_FAIL;
97 	}
98 
99 	rad_chap_encode(request->packet, pass_str, chap->vp_octets[0], password);
100 
101 	if (RDEBUG_ENABLED3) {
102 		uint8_t const *p;
103 		size_t length;
104 		VALUE_PAIR *vp;
105 		char buffer[MAX_STRING_LEN * 2 + 1];
106 
107 		RDEBUG3("Comparing with \"known good\" &control:Cleartext-Password value \"%s\"",
108 			password->vp_strvalue);
109 
110 		vp = fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY);
111 		if (vp) {
112 			RDEBUG2("Using challenge from &request:CHAP-Challenge");
113 			p = vp->vp_octets;
114 			length = vp->vp_length;
115 		} else {
116 			RDEBUG2("Using challenge from authenticator field");
117 			p = request->packet->vector;
118 			length = sizeof(request->packet->vector);
119 		}
120 
121 		fr_bin2hex(buffer, p, length);
122 		RINDENT();
123 		RDEBUG3("CHAP challenge : %s", buffer);
124 
125 		fr_bin2hex(buffer, chap->vp_octets + 1, CHAP_VALUE_LENGTH);
126 		RDEBUG3("Client sent    : %s", buffer);
127 
128 		fr_bin2hex(buffer, pass_str + 1, CHAP_VALUE_LENGTH);
129 		RDEBUG3("We calculated  : %s", buffer);
130 		REXDENT();
131 	} else {
132 		RDEBUG2("Comparing with \"known good\" Cleartext-Password");
133 	}
134 
135 	if (rad_digest_cmp(pass_str + 1, chap->vp_octets + 1, CHAP_VALUE_LENGTH) != 0) {
136 		REDEBUG("Password comparison failed: password is incorrect");
137 		return RLM_MODULE_REJECT;
138 	}
139 
140 	RDEBUG("CHAP user \"%s\" authenticated successfully", request->username->vp_strvalue);
141 
142 	return RLM_MODULE_OK;
143 }
144 
145 /*
146  *	The module name should be the only globally exported symbol.
147  *	That is, everything else should be 'static'.
148  *
149  *	If the module needs to temporarily modify it's instantiation
150  *	data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
151  *	The server will then take care of ensuring that the module
152  *	is single-threaded.
153  */
154 extern module_t rlm_chap;
155 module_t rlm_chap = {
156 	.magic		= RLM_MODULE_INIT,
157 	.name		= "chap",
158 	.methods = {
159 		[MOD_AUTHENTICATE]	= mod_authenticate,
160 		[MOD_AUTHORIZE]		= mod_authorize,
161 	},
162 };
163