6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Copyright 2001 The FreeRADIUS server project
21 * Copyright 2001 Kostas Kalevras <kkalev@noc.ntua.gr>
25 #include "libradius.h"
35 #include "../../include/md5.h"
36 #include "../../include/sha1.h"
38 #define PAP_ENC_INVALID -1
39 #define PAP_ENC_CLEAR 0
40 #define PAP_ENC_CRYPT 1
42 #define PAP_ENC_SHA1 3
46 #define PAP_INST_FREE(inst) \
47 free((char *)inst->scheme); \
50 static const char rcsid[] = "$Id$";
53 * Define a structure for our module configuration.
55 * These variables do not need to be in a structure, but it's
56 * a lot cleaner to do so, and a pointer to the structure can
57 * be used as the instance handle.
59 typedef struct rlm_pap_t {
60 char *scheme; /* password encryption scheme */
65 * A mapping of configuration file names to internal variables.
67 * Note that the string is dynamically allocated, so it MUST
68 * be freed. When the configuration file parse re-reads the string,
69 * it free's the old one, and strdup's the new one, placing the pointer
70 * to the strdup'd string into 'config.string'. This gets around
73 static CONF_PARSER module_config[] = {
74 { "encryption_scheme", PW_TYPE_STRING_PTR, offsetof(rlm_pap_t,scheme), NULL, "crypt" },
75 { NULL, -1, 0, NULL, NULL }
78 static const char *pap_hextab = "0123456789abcdef";
81 * Smaller & faster than snprintf("%x");
82 * Completely stolen from ns_mta_md5 module
84 static void pap_hexify(char *buffer, char *str, int len)
90 for(i = 0;i < len; i ++) {
92 buffer[2*i] = pap_hextab[(ch>>4) & 15];
93 buffer[2*i + 1] = pap_hextab[ch & 15];
98 static int pap_instantiate(CONF_SECTION *conf, void **instance)
103 * Set up a storage area for instance data
105 inst = rad_malloc(sizeof(*inst));
109 memset(inst, 0, sizeof(*inst));
112 * If the configuration parameters can't be parsed, then
115 if (cf_section_parse(conf, inst, module_config) < 0) {
119 inst->sch = PAP_ENC_INVALID;
120 if (inst->scheme == NULL || strlen(inst->scheme) == 0){
121 radlog(L_ERR, "rlm_pap: Wrong password scheme passed");
125 if (strcasecmp(inst->scheme,"clear") == 0)
126 inst->sch = PAP_ENC_CLEAR;
127 else if (strcasecmp(inst->scheme,"crypt") == 0){
128 inst->sch = PAP_ENC_CRYPT;
130 else if (strcasecmp(inst->scheme,"md5") == 0)
131 inst->sch = PAP_ENC_MD5;
132 else if (strcasecmp(inst->scheme,"sha1") == 0)
133 inst->sch = PAP_ENC_SHA1;
134 else if (strcasecmp(inst->scheme,"nt") == 0)
135 inst->sch = PAP_ENC_NT;
137 radlog(L_ERR, "rlm_pap: Wrong password scheme passed");
148 * Find the named user in this modules database. Create the set
149 * of attribute-value pairs to check and reply with for this user
150 * from the database. The authentication code only needs to check
151 * the password, the rest is done here.
153 static int pap_authenticate(void *instance, REQUEST *request)
155 VALUE_PAIR *passwd_item;
156 VALUE_PAIR *module_fmsg_vp;
157 char module_fmsg[MAX_STRING_LEN];
159 SHA1_CTX sha1_context;
160 unsigned char digest[20];
161 char buff[MAX_STRING_LEN];
162 rlm_pap_t *inst = (rlm_pap_t *) instance;
164 /* quiet the compiler */
168 if(!request->username){
169 radlog(L_AUTH, "rlm_pap: Attribute \"User-Name\" is required for authentication.\n");
170 return RLM_MODULE_INVALID;
173 if (!request->password){
174 radlog(L_AUTH, "rlm_pap: Attribute \"Password\" is required for authentication.");
175 return RLM_MODULE_INVALID;
178 if (request->password->attribute != PW_PASSWORD) {
179 radlog(L_AUTH, "rlm_pap: Attribute \"Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
180 return RLM_MODULE_INVALID;
183 if (request->password->length == 0) {
184 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: empty password supplied");
185 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
186 pairadd(&request->packet->vps, module_fmsg_vp);
187 return RLM_MODULE_INVALID;
190 DEBUG("rlm_pap: login attempt by \"%s\" with password %s",
191 request->username->strvalue, request->password->strvalue);
193 if ((((passwd_item = pairfind(request->config_items, PW_PASSWORD)) == NULL) &&
194 ((passwd_item = pairfind(request->config_items, PW_CRYPT_PASSWORD)) == NULL)) ||
195 (passwd_item->length == 0) || (passwd_item->strvalue[0] == 0)) {
196 DEBUG("rlm_pap: No password (or empty password) to check against for user %s",request->username->strvalue);
197 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: User password not available");
198 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
199 pairadd(&request->packet->vps, module_fmsg_vp);
200 return RLM_MODULE_INVALID;
202 if (passwd_item->attribute == PW_CRYPT_PASSWORD){
203 if (inst->sch != PAP_ENC_CRYPT){
204 radlog(L_ERR, "rlm_pap: Crypt-Password attribute but encryption scheme is not set to CRYPT");
205 return RLM_MODULE_FAIL;
209 DEBUG("rlm_pap: Using password \"%s\" for user %s authentication.",
210 passwd_item->strvalue, request->username->strvalue);
212 if (inst->sch == PAP_ENC_INVALID || inst->sch > PAP_MAX_ENC){
213 radlog(L_ERR, "rlm_pap: Wrong password scheme");
214 return RLM_MODULE_FAIL;
218 radlog(L_ERR, "rlm_pap: Wrong password scheme");
219 return RLM_MODULE_FAIL;
222 DEBUG("rlm_pap: Using clear text password.");
223 if (strcmp((char *) passwd_item->strvalue,
224 (char *) request->password->strvalue) != 0){
225 DEBUG("rlm_pap: Passwords don't match");
226 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CLEAR TEXT password check failed");
227 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
228 pairadd(&request->packet->vps, module_fmsg_vp);
229 return RLM_MODULE_REJECT;
233 DEBUG("rlm_pap: Using CRYPT encryption.");
234 if (lrad_crypt_check((char *) request->password->strvalue,
235 (char *) passwd_item->strvalue) != 0) {
236 DEBUG("rlm_pap: Passwords don't match");
237 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CRYPT password check failed");
238 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
239 pairadd(&request->packet->vps, module_fmsg_vp);
240 return RLM_MODULE_REJECT;
244 DEBUG("rlm_pap: Using MD5 encryption.");
246 if (passwd_item->length != 32) {
247 DEBUG("rlm_pap: Configured MD5 password has incorrect length");
248 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured MD5 password has incorrect length");
249 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
250 pairadd(&request->packet->vps, module_fmsg_vp);
251 return RLM_MODULE_REJECT;
254 MD5Init(&md5_context);
255 MD5Update(&md5_context, request->password->strvalue, request->password->length);
256 MD5Final(digest, &md5_context);
257 pap_hexify(buff,digest,16);
259 if (strcmp((char *)passwd_item->strvalue, buff) != 0){
260 DEBUG("rlm_pap: Passwords don't match");
261 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: MD5 password check failed");
262 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
263 pairadd(&request->packet->vps, module_fmsg_vp);
264 return RLM_MODULE_REJECT;
269 DEBUG("rlm_pap: Using SHA1 encryption.");
271 if (passwd_item->length != 40) {
272 DEBUG("rlm_pap: Configured SHA1 password has incorrect length");
273 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured SHA1 password has incorrect length");
274 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
275 pairadd(&request->packet->vps, module_fmsg_vp);
276 return RLM_MODULE_REJECT;
279 SHA1Init(&sha1_context);
280 SHA1Update(&sha1_context, request->password->strvalue, request->password->length);
281 SHA1Final(digest,&sha1_context);
282 pap_hexify(buff,digest,20);
284 if (strcmp((char *)passwd_item->strvalue, buff) != 0){
285 DEBUG("rlm_pap: Passwords don't match");
286 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: SHA1 password check failed");
287 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
288 pairadd(&request->packet->vps, module_fmsg_vp);
289 return RLM_MODULE_REJECT;
293 DEBUG("rlm_pap: Using NT HASH encryption.");
295 if (passwd_item->length != 32) {
296 DEBUG("rlm_pap: Configured NT password has incorrect length");
297 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured NT password has incorrect length");
298 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
299 pairadd(&request->packet->vps, module_fmsg_vp);
300 return RLM_MODULE_REJECT;
302 char szUnicodePass[513];
307 * NT passwords are unicode. Convert plain text password
308 * to unicode by inserting a zero every other byte
310 nPasswordLen = strlen(request->password->strvalue);
311 for (i = 0; i < nPasswordLen; i++) {
312 szUnicodePass[i << 1] = request->password->strvalue[i];
313 szUnicodePass[(i << 1) + 1] = 0;
316 /* Encrypt Unicode password to a 16-byte MD4 hash */
317 md4_calc(digest, szUnicodePass, (nPasswordLen<<1) );
319 pap_hexify(buff,digest,16);
322 if (strcmp((char *)passwd_item->strvalue, buff) != 0){
323 DEBUG("rlm_pap: Passwords don't match");
324 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: NT HASH password check failed");
325 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
326 pairadd(&request->packet->vps, module_fmsg_vp);
327 return RLM_MODULE_REJECT;
332 DEBUG("rlm_pap: User authenticated succesfully");
334 return RLM_MODULE_OK;
337 static int pap_detach(void *instance)
339 rlm_pap_t *inst = (rlm_pap_t *) instance;
347 * The module name should be the only globally exported symbol.
348 * That is, everything else should be 'static'.
350 * If the module needs to temporarily modify it's instantiation
351 * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
352 * The server will then take care of ensuring that the module
353 * is single-threaded.
358 NULL, /* initialization */
359 pap_instantiate, /* instantiation */
361 pap_authenticate, /* authentication */
362 NULL, /* authorization */
363 NULL, /* preaccounting */
364 NULL, /* accounting */
365 NULL, /* checksimul */
366 NULL, /* pre-proxy */
367 NULL, /* post-proxy */
370 pap_detach, /* detach */