#define PAP_ENC_MD5 2
#define PAP_ENC_SHA1 3
#define PAP_ENC_NT 4
-#define PAP_MAX_ENC 4
+#define PAP_ENC_LM 5
+#define PAP_ENC_SMD5 6
+#define PAP_ENC_SSHA 7
+#define PAP_ENC_NS_MTA_MD5 8
+#define PAP_ENC_AUTO 9
+#define PAP_MAX_ENC 9
-#define PAP_INST_FREE(inst) \
- free((char *)inst->scheme); \
- free(inst)
static const char rcsid[] = "$Id$";
* be used as the instance handle.
*/
typedef struct rlm_pap_t {
+ const char *name; /* CONF_SECTION->name, not strdup'd */
char *scheme; /* password encryption scheme */
int sch;
+ char norm_passwd;
+ int auto_header;
} rlm_pap_t;
/*
*/
static CONF_PARSER module_config[] = {
{ "encryption_scheme", PW_TYPE_STRING_PTR, offsetof(rlm_pap_t,scheme), NULL, "crypt" },
+ { "auto_header", PW_TYPE_BOOLEAN, offsetof(rlm_pap_t,auto_header), NULL, "no" },
{ NULL, -1, 0, NULL, NULL }
};
-static const char *pap_hextab = "0123456789abcdef";
+static const LRAD_NAME_NUMBER schemes[] = {
+ { "clear", PAP_ENC_CLEAR },
+ { "crypt", PAP_ENC_CRYPT },
+ { "md5", PAP_ENC_MD5 },
+ { "sha1", PAP_ENC_SHA1 },
+ { "nt", PAP_ENC_NT },
+ { "lm", PAP_ENC_LM },
+ { "smd5", PAP_ENC_SMD5 },
+ { "ssha", PAP_ENC_SSHA },
+ { "auto", PAP_ENC_AUTO },
+ { NULL, PAP_ENC_INVALID }
+};
+
/*
- * Smaller & faster than snprintf("%x");
- * Completely stolen from ns_mta_md5 module
+ * For auto-header discovery.
*/
-static void pap_hexify(char *buffer, char *str, int len)
+static const LRAD_NAME_NUMBER header_names[] = {
+ { "{clear}", PW_CLEARTEXT_PASSWORD },
+ { "{cleartext}", PW_CLEARTEXT_PASSWORD },
+ { "{md5}", PW_MD5_PASSWORD },
+ { "{smd5}", PW_SMD5_PASSWORD },
+ { "{crypt}", PW_CRYPT_PASSWORD },
+ { "{sha}", PW_SHA_PASSWORD },
+ { "{ssha}", PW_SSHA_PASSWORD },
+ { "{nt}", PW_NT_PASSWORD },
+ { "{x-nthash}", PW_NT_PASSWORD },
+ { "{ns-mta-md5}", PW_NS_MTA_MD5_PASSWORD },
+ { NULL, 0 }
+};
+
+
+static int pap_detach(void *instance)
{
- char *pch = str;
- char ch;
- int i;
+ rlm_pap_t *inst = (rlm_pap_t *) instance;
- for(i = 0;i < len; i ++) {
- ch = pch[i];
- buffer[2*i] = pap_hextab[(ch>>4) & 15];
- buffer[2*i + 1] = pap_hextab[ch & 15];
- }
- return;
+ free((char *)inst->scheme);
+ free(inst);
+
+ return 0;
}
+
static int pap_instantiate(CONF_SECTION *conf, void **instance)
{
rlm_pap_t *inst;
* fail.
*/
if (cf_section_parse(conf, inst, module_config) < 0) {
- free(inst);
+ pap_detach(inst);
return -1;
}
- inst->sch = PAP_ENC_INVALID;
+
if (inst->scheme == NULL || strlen(inst->scheme) == 0){
- radlog(L_ERR, "rlm_pap: Wrong password scheme passed");
- PAP_INST_FREE(inst);
+ radlog(L_ERR, "rlm_pap: No scheme defined");
+ pap_detach(inst);
return -1;
}
- if (strcasecmp(inst->scheme,"clear") == 0)
- inst->sch = PAP_ENC_CLEAR;
- else if (strcasecmp(inst->scheme,"crypt") == 0){
- inst->sch = PAP_ENC_CRYPT;
- }
- else if (strcasecmp(inst->scheme,"md5") == 0)
- inst->sch = PAP_ENC_MD5;
- else if (strcasecmp(inst->scheme,"sha1") == 0)
- inst->sch = PAP_ENC_SHA1;
- else if (strcasecmp(inst->scheme,"nt") == 0)
- inst->sch = PAP_ENC_NT;
- else{
- radlog(L_ERR, "rlm_pap: Wrong password scheme passed");
- PAP_INST_FREE(inst);
+
+ inst->sch = lrad_str2int(schemes, inst->scheme, PAP_ENC_INVALID);
+ if (inst->sch == PAP_ENC_INVALID) {
+ radlog(L_ERR, "rlm_pap: Unknown scheme \"%s\"", inst->scheme);
+ pap_detach(inst);
return -1;
}
*instance = inst;
+ inst->name = cf_section_name2(conf);
+ if (!inst->name) {
+ inst->name = cf_section_name1(conf);
+ }
return 0;
}
+
+/*
+ * Decode one base64 chunk
+ */
+static int decode_it(const char *src, uint8_t *dst)
+{
+ int i;
+ unsigned int x = 0;
+
+ for(i = 0; i < 4; i++) {
+ if (src[i] >= 'A' && src[i] <= 'Z')
+ x = (x << 6) + (unsigned int)(src[i] - 'A' + 0);
+ else if (src[i] >= 'a' && src[i] <= 'z')
+ x = (x << 6) + (unsigned int)(src[i] - 'a' + 26);
+ else if(src[i] >= '0' && src[i] <= '9')
+ x = (x << 6) + (unsigned int)(src[i] - '0' + 52);
+ else if(src[i] == '+')
+ x = (x << 6) + 62;
+ else if (src[i] == '/')
+ x = (x << 6) + 63;
+ else if (src[i] == '=')
+ x = (x << 6);
+ else return 0;
+ }
+
+ dst[2] = (unsigned char)(x & 255); x >>= 8;
+ dst[1] = (unsigned char)(x & 255); x >>= 8;
+ dst[0] = (unsigned char)(x & 255); x >>= 8;
+
+ return 1;
+}
+
+
+/*
+ * Base64 decoding.
+ */
+static int base64_decode (const char *src, uint8_t *dst)
+{
+ int length, equals;
+ int i, num;
+ uint8_t last[3];
+
+ length = equals = 0;
+ while (src[length] && src[length] != '=') length++;
+
+ if (src[length] != '=') return 0; /* no trailing '=' */
+
+ while (src[length + equals] == '=') equals++;
+
+ num = (length + equals) / 4;
+
+ for (i = 0; i < num - 1; i++) {
+ if (!decode_it(src, dst)) return 0;
+ src += 4;
+ dst += 3;
+ }
+
+ decode_it(src, last);
+ for (i = 0; i < (3 - equals); i++) {
+ dst[i] = last[i];
+ }
+
+ return (num * 3) - equals;
+}
+
+
/*
- * Find the named user in this modules database. Create the set
- * of attribute-value pairs to check and reply with for this user
- * from the database. The authentication code only needs to check
- * the password, the rest is done here.
+ * Hex or base64 or bin auto-discovery.
+ */
+static void normify(VALUE_PAIR *vp, int min_length)
+{
+ int decoded;
+ char buffer[64];
+
+ if ((size_t) min_length >= sizeof(buffer)) return; /* paranoia */
+
+ /*
+ * Hex encoding.
+ */
+ if (vp->length >= (2 * min_length)) {
+ decoded = lrad_hex2bin(vp->strvalue, buffer, vp->length >> 1);
+ if (decoded == (vp->length >> 1)) {
+ DEBUG2("rlm_pap: Normalizing %s from hex encoding", vp->name);
+ memcpy(vp->strvalue, buffer, decoded);
+ vp->length = decoded;
+ return;
+ }
+ }
+
+ /*
+ * Base 64 encoding. It's at least 4/3 the original size,
+ * and we want to avoid division...
+ */
+ if ((vp->length * 3) >= ((min_length * 4))) {
+ decoded = base64_decode(vp->strvalue, buffer);
+ if (decoded >= min_length) {
+ DEBUG2("rlm_pap: Normalizing %s from base64 encoding", vp->name);
+ memcpy(vp->strvalue, buffer, decoded);
+ vp->length = decoded;
+ return;
+ }
+ }
+
+ /*
+ * Else unknown encoding, or already binary. Leave it.
+ */
+}
+
+
+/*
+ * Authorize the user for PAP authentication.
+ *
+ * This isn't strictly necessary, but it does make the
+ * server simpler to configure.
+ */
+static int pap_authorize(void *instance, REQUEST *request)
+{
+ rlm_pap_t *inst = instance;
+ int auth_type = FALSE;
+ int found_pw = FALSE;
+ int user_pw = FALSE;
+ VALUE_PAIR *vp;
+ VALUE_PAIR *cleartext_pw = NULL;
+
+ for (vp = request->config_items; vp != NULL; vp = vp->next) {
+ switch (vp->attribute) {
+ case PW_USER_PASSWORD: /* deprecated */
+ user_pw = TRUE;
+ found_pw = TRUE;
+
+ /*
+ * Look for '{foo}', and use them
+ */
+ if (!inst->auto_header ||
+ (vp->strvalue[0] != '{')) {
+ break;
+ }
+ /* FALL-THROUGH */
+
+ case PW_PASSWORD_WITH_HEADER: /* preferred */
+ {
+ int attr;
+ uint8_t *p, *q;
+ char buffer[64];
+ VALUE_PAIR *new_vp;
+
+ found_pw = TRUE;
+ q = vp->strvalue;
+ p = strchr(q + 1, '}');
+ if (!p) {
+ /*
+ * FIXME: Turn it into a
+ * cleartext-password, unless it,
+ * or user-password already
+ * exists.
+ */
+ break;
+ }
+
+ if ((size_t) (p - q) > sizeof(buffer)) break;
+
+ memcpy(buffer, q, p - q + 1);
+ buffer[p - q + 1] = '\0';
+
+ attr = lrad_str2int(header_names, buffer, 0);
+ if (!attr) {
+ DEBUG2("rlm_pap: Found unknown header {%s}: Not doing anything", buffer);
+ break;
+ }
+
+ new_vp = paircreate(attr, PW_TYPE_STRING);
+ if (!new_vp) break; /* OOM */
+
+ strcpy(new_vp->strvalue, p + 1);/* bounds OK */
+ new_vp->length = strlen(new_vp->strvalue);
+ pairadd(&request->config_items, new_vp);
+
+ /*
+ * May be old-style User-Password with header.
+ * We've found the header & created the proper
+ * attribute, so we should delete the old
+ * User-Password here.
+ */
+ pairdelete(&request->config_items, PW_USER_PASSWORD);
+ }
+ break;
+
+ case PW_CLEARTEXT_PASSWORD:
+ cleartext_pw = vp;
+
+ case PW_CRYPT_PASSWORD:
+ case PW_NS_MTA_MD5_PASSWORD:
+ found_pw = TRUE;
+ break; /* don't touch these */
+
+ case PW_MD5_PASSWORD:
+ case PW_SMD5_PASSWORD:
+ case PW_NT_PASSWORD:
+ case PW_LM_PASSWORD:
+ normify(vp, 16); /* ensure it's in the right format */
+ found_pw = TRUE;
+ break;
+
+ case PW_SHA_PASSWORD:
+ case PW_SSHA_PASSWORD:
+ normify(vp, 20); /* ensure it's in the right format */
+ found_pw = TRUE;
+ break;
+
+ /*
+ * If it's proxied somewhere, don't complain
+ * about not having passwords or Auth-Type.
+ */
+ case PW_PROXY_TO_REALM:
+ {
+ REALM *realm = realm_find(vp->strvalue, 0);
+ if (realm &&
+ (realm->ipaddr != htonl(INADDR_NONE))) {
+ return RLM_MODULE_NOOP;
+ }
+ break;
+ }
+
+ case PW_AUTH_TYPE:
+ auth_type = TRUE;
+
+ /*
+ * Auth-Type := Accept
+ * Auth-Type := Reject
+ */
+ if ((vp->lvalue == 254) ||
+ (vp->lvalue == 4)) {
+ found_pw = 1;
+ }
+ break;
+
+ default:
+ break; /* ignore it */
+
+ }
+ }
+
+ /*
+ * Print helpful warnings if there was no password.
+ */
+ if (!found_pw) {
+ DEBUG("rlm_pap: WARNING! No \"known good\" password found for the user. Authentication may fail because of this.");
+ return RLM_MODULE_NOOP;
+ }
+
+ /*
+ * For backwards compatibility with all of the other
+ * modules, copy Cleartext-Password to User-Password
+ */
+ if (cleartext_pw && !user_pw) {
+ vp = paircreate(PW_USER_PASSWORD, PW_TYPE_STRING);
+ if (!vp) return RLM_MODULE_FAIL;
+
+ memcpy(vp, cleartext_pw, sizeof(*vp));
+ vp->next = NULL;
+ pairadd(&request->config_items, vp);
+ }
+
+ /*
+ * Don't touch existing Auth-Types.
+ */
+ if (auth_type) {
+ DEBUG2("rlm_pap: Found existing Auth-Type, not changing it.");
+ return RLM_MODULE_NOOP;
+ }
+
+ /*
+ * Can't do PAP if there's no password.
+ */
+ if (!request->password ||
+ (request->password->attribute != PW_USER_PASSWORD)) {
+ /*
+ * Don't print out debugging messages if we know
+ * they're useless.
+ */
+ if (request->packet->code == PW_ACCESS_CHALLENGE) {
+ return RLM_MODULE_NOOP;
+ }
+
+ DEBUG2("rlm_pap: No clear-text password in the request. Not performing PAP.");
+ return RLM_MODULE_NOOP;
+ }
+
+ vp = paircreate(PW_AUTH_TYPE, PW_TYPE_INTEGER);
+ if (!vp) return RLM_MODULE_FAIL;
+ pairparsevalue(vp, inst->name);
+
+ pairadd(&request->config_items, vp);
+
+ return RLM_MODULE_UPDATED;
+}
+
+
+/*
+ * Authenticate the user via one of any well-known password.
*/
static int pap_authenticate(void *instance, REQUEST *request)
{
- VALUE_PAIR *passwd_item;
+ rlm_pap_t *inst = instance;
+ VALUE_PAIR *vp;
VALUE_PAIR *module_fmsg_vp;
char module_fmsg[MAX_STRING_LEN];
MD5_CTX md5_context;
SHA1_CTX sha1_context;
- unsigned char digest[20];
+ uint8_t digest[40];
char buff[MAX_STRING_LEN];
- rlm_pap_t *inst = (rlm_pap_t *) instance;
-
- /* quiet the compiler */
- instance = instance;
- request = request;
-
- if(!request->username){
- radlog(L_AUTH, "rlm_pap: Attribute \"User-Name\" is required for authentication.\n");
- return RLM_MODULE_INVALID;
- }
+ char buff2[MAX_STRING_LEN + 50];
+ int scheme = PAP_ENC_INVALID;
if (!request->password){
radlog(L_AUTH, "rlm_pap: Attribute \"Password\" is required for authentication.");
return RLM_MODULE_INVALID;
}
- if (request->password->attribute != PW_PASSWORD) {
- radlog(L_AUTH, "rlm_pap: Attribute \"Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
+ /*
+ * Clear-text passwords are the only ones we support.
+ */
+ if (request->password->attribute != PW_USER_PASSWORD) {
+ radlog(L_AUTH, "rlm_pap: Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
return RLM_MODULE_INVALID;
}
+ /*
+ * The user MUST supply a non-zero-length password.
+ */
if (request->password->length == 0) {
snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: empty password supplied");
module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
return RLM_MODULE_INVALID;
}
- DEBUG("rlm_pap: login attempt by \"%s\" with password %s",
- request->username->strvalue, request->password->strvalue);
+ DEBUG("rlm_pap: login attempt with password %s",
+ request->password->strvalue);
+
+ /*
+ * First, auto-detect passwords, by attribute in the
+ * config items.
+ */
+ if ((inst->sch == PAP_ENC_AUTO) || inst->auto_header) {
+ for (vp = request->config_items; vp != NULL; vp = vp->next) {
+ switch (vp->attribute) {
+ case PW_USER_PASSWORD: /* deprecated */
+ case PW_CLEARTEXT_PASSWORD: /* preferred */
+ goto do_clear;
+
+ case PW_CRYPT_PASSWORD:
+ goto do_crypt;
+
+ case PW_MD5_PASSWORD:
+ goto do_md5;
+
+ case PW_SHA_PASSWORD:
+ goto do_sha;
+
+ case PW_NT_PASSWORD:
+ goto do_nt;
- if ((((passwd_item = pairfind(request->config_items, PW_PASSWORD)) == NULL) &&
- ((passwd_item = pairfind(request->config_items, PW_CRYPT_PASSWORD)) == NULL)) ||
- (passwd_item->length == 0) || (passwd_item->strvalue[0] == 0)) {
- DEBUG("rlm_pap: No password (or empty password) to check against for user %s",request->username->strvalue);
- snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: User password not available");
- module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
- pairadd(&request->packet->vps, module_fmsg_vp);
- return RLM_MODULE_INVALID;
- }
- if (passwd_item->attribute == PW_CRYPT_PASSWORD){
- if (inst->sch != PAP_ENC_CRYPT){
- radlog(L_ERR, "rlm_pap: Crypt-Password attribute but encryption scheme is not set to CRYPT");
- return RLM_MODULE_FAIL;
- }
- }
+ case PW_LM_PASSWORD:
+ goto do_lm;
- DEBUG("rlm_pap: Using password \"%s\" for user %s authentication.",
- passwd_item->strvalue, request->username->strvalue);
+ case PW_SMD5_PASSWORD:
+ goto do_smd5;
- if (inst->sch == PAP_ENC_INVALID || inst->sch > PAP_MAX_ENC){
- radlog(L_ERR, "rlm_pap: Wrong password scheme");
- return RLM_MODULE_FAIL;
- }
- switch(inst->sch){
- default:
- radlog(L_ERR, "rlm_pap: Wrong password scheme");
- return RLM_MODULE_FAIL;
- break;
- case PAP_ENC_CLEAR:
- DEBUG("rlm_pap: Using clear text password.");
- if (strcmp((char *) passwd_item->strvalue,
- (char *) request->password->strvalue) != 0){
- DEBUG("rlm_pap: Passwords don't match");
- snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CLEAR TEXT password check failed");
- module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
- pairadd(&request->packet->vps, module_fmsg_vp);
- return RLM_MODULE_REJECT;
- }
- break;
- case PAP_ENC_CRYPT:
- DEBUG("rlm_pap: Using CRYPT encryption.");
- if (lrad_crypt_check((char *) request->password->strvalue,
- (char *) passwd_item->strvalue) != 0) {
- DEBUG("rlm_pap: Passwords don't match");
- snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CRYPT password check failed");
- module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
- pairadd(&request->packet->vps, module_fmsg_vp);
- return RLM_MODULE_REJECT;
- }
- break;
- case PAP_ENC_MD5:
- DEBUG("rlm_pap: Using MD5 encryption.");
-
- if (passwd_item->length != 32) {
- DEBUG("rlm_pap: Configured MD5 password has incorrect length");
- snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured MD5 password has incorrect length");
- module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
- pairadd(&request->packet->vps, module_fmsg_vp);
- return RLM_MODULE_REJECT;
- }
+ case PW_SSHA_PASSWORD:
+ goto do_ssha;
- MD5Init(&md5_context);
- MD5Update(&md5_context, request->password->strvalue, request->password->length);
- MD5Final(digest, &md5_context);
- pap_hexify(buff,digest,16);
- buff[32] = '\0';
- if (strcmp((char *)passwd_item->strvalue, buff) != 0){
- DEBUG("rlm_pap: Passwords don't match");
- snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: MD5 password check failed");
- module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
- pairadd(&request->packet->vps, module_fmsg_vp);
- return RLM_MODULE_REJECT;
- }
- break;
- case PAP_ENC_SHA1:
-
- DEBUG("rlm_pap: Using SHA1 encryption.");
-
- if (passwd_item->length != 40) {
- DEBUG("rlm_pap: Configured SHA1 password has incorrect length");
- snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured SHA1 password has incorrect length");
- module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
- pairadd(&request->packet->vps, module_fmsg_vp);
- return RLM_MODULE_REJECT;
- }
+ case PW_NS_MTA_MD5_PASSWORD:
+ goto do_ns_mta_md5;
- SHA1Init(&sha1_context);
- SHA1Update(&sha1_context, request->password->strvalue, request->password->length);
- SHA1Final(digest,&sha1_context);
- pap_hexify(buff,digest,20);
- buff[40] = '\0';
- if (strcmp((char *)passwd_item->strvalue, buff) != 0){
- DEBUG("rlm_pap: Passwords don't match");
- snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: SHA1 password check failed");
- module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
- pairadd(&request->packet->vps, module_fmsg_vp);
- return RLM_MODULE_REJECT;
- }
- break;
- case PAP_ENC_NT:
- DEBUG("rlm_pap: Using NT HASH encryption.");
-
- if (passwd_item->length != 32) {
- DEBUG("rlm_pap: Configured NT password has incorrect length");
- snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured NT password has incorrect length");
- module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
- pairadd(&request->packet->vps, module_fmsg_vp);
- return RLM_MODULE_REJECT;
- } else {
- char szUnicodePass[513];
- int nPasswordLen;
- int i;
+ default:
+ break; /* ignore it */
- /*
- * NT passwords are unicode. Convert plain text password
- * to unicode by inserting a zero every other byte
- */
- nPasswordLen = strlen(request->password->strvalue);
- for (i = 0; i < nPasswordLen; i++) {
- szUnicodePass[i << 1] = request->password->strvalue[i];
- szUnicodePass[(i << 1) + 1] = 0;
- }
-
- /* Encrypt Unicode password to a 16-byte MD4 hash */
- md4_calc(digest, szUnicodePass, (nPasswordLen<<1) );
-
- pap_hexify(buff,digest,16);
- buff[32] = '\0';
}
- if (strcmp((char *)passwd_item->strvalue, buff) != 0){
- DEBUG("rlm_pap: Passwords don't match");
- snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: NT HASH password check failed");
- module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
- pairadd(&request->packet->vps, module_fmsg_vp);
- return RLM_MODULE_REJECT;
- }
- break;
- }
+ }
- DEBUG("rlm_pap: User authenticated succesfully");
+ fail:
+ DEBUG("rlm_pap: No password configured for the user. Cannot do authentication");
+ return RLM_MODULE_FAIL;
- return RLM_MODULE_OK;
-}
+ } else {
+ vp = NULL;
+
+ if (inst->sch == PAP_ENC_CRYPT) {
+ vp = pairfind(request->config_items, PW_CRYPT_PASSWORD);
+ }
+
+ /*
+ * Old-style: all passwords are in User-Password.
+ */
+ if (!vp) {
+ vp = pairfind(request->config_items, PW_USER_PASSWORD);
+ if (!vp) goto fail;
+ }
+ scheme = inst->sch;
+ }
-static int pap_detach(void *instance)
-{
- rlm_pap_t *inst = (rlm_pap_t *) instance;
+ /*
+ * Now that we've decided what to do, go do it.
+ */
+ switch (scheme) {
+ case PAP_ENC_CLEAR:
+ do_clear:
+ DEBUG("rlm_pap: Using clear text password.");
+ if (strcmp((char *) vp->strvalue,
+ (char *) request->password->strvalue) != 0){
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CLEAR TEXT password check failed");
+ goto make_msg;
+ }
+ done:
+ DEBUG("rlm_pap: User authenticated succesfully");
+ return RLM_MODULE_OK;
+ break;
+
+ case PAP_ENC_CRYPT:
+ do_crypt:
+ DEBUG("rlm_pap: Using CRYPT encryption.");
+ if (lrad_crypt_check((char *) request->password->strvalue,
+ (char *) vp->strvalue) != 0) {
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CRYPT password check failed");
+ goto make_msg;
+ }
+ goto done;
+ break;
+
+ case PW_MD5_PASSWORD:
+ do_md5:
+ DEBUG("rlm_pap: Using MD5 encryption.");
+
+ normify(vp, 16);
+ if (vp->length != 16) {
+ DEBUG("rlm_pap: Configured MD5 password has incorrect length");
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured MD5 password has incorrect length");
+ goto make_msg;
+ }
+
+ MD5Init(&md5_context);
+ MD5Update(&md5_context, request->password->strvalue,
+ request->password->length);
+ MD5Final(digest, &md5_context);
+ if (memcmp(digest, vp->strvalue, vp->length) != 0) {
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: MD5 password check failed");
+ goto make_msg;
+ }
+ goto done;
+ break;
+
+ case PW_SMD5_PASSWORD:
+ do_smd5:
+ DEBUG("rlm_pap: Using SMD5 encryption.");
+
+ normify(vp, 16);
+ if (vp->length <= 16) {
+ DEBUG("rlm_pap: Configured SMD5 password has incorrect length");
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured SMD5 password has incorrect length");
+ goto make_msg;
+ }
+
+ MD5Init(&md5_context);
+ MD5Update(&md5_context, request->password->strvalue,
+ request->password->length);
+ MD5Update(&md5_context, &vp->strvalue[16], vp->length - 16);
+ MD5Final(digest, &md5_context);
+
+ /*
+ * Compare only the MD5 hash results, not the salt.
+ */
+ if (memcmp(digest, vp->strvalue, 16) != 0) {
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: SMD5 password check failed");
+ goto make_msg;
+ }
+ goto done;
+ break;
+
+ case PW_SHA_PASSWORD:
+ do_sha:
+ DEBUG("rlm_pap: Using SHA1 encryption.");
+
+ normify(vp, 20);
+ if (vp->length != 20) {
+ DEBUG("rlm_pap: Configured SHA1 password has incorrect length");
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured SHA1 password has incorrect length");
+ goto make_msg;
+ }
+
+ SHA1Init(&sha1_context);
+ SHA1Update(&sha1_context, request->password->strvalue,
+ request->password->length);
+ SHA1Final(digest,&sha1_context);
+ if (memcmp(digest, vp->strvalue, vp->length) != 0) {
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: SHA1 password check failed");
+ goto make_msg;
+ }
+ goto done;
+ break;
+
+ case PW_SSHA_PASSWORD:
+ do_ssha:
+ DEBUG("rlm_pap: Using SSHA encryption.");
+
+ normify(vp, 20);
+ if (vp->length <= 20) {
+ DEBUG("rlm_pap: Configured SSHA password has incorrect length");
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured SHA password has incorrect length");
+ goto make_msg;
+ }
+
+
+ SHA1Init(&sha1_context);
+ SHA1Update(&sha1_context, request->password->strvalue,
+ request->password->length);
+ SHA1Update(&sha1_context, &vp->strvalue[20], vp->length - 20);
+ SHA1Final(digest,&sha1_context);
+ if (memcmp(digest, vp->strvalue, 20) != 0) {
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: SSHA password check failed");
+ goto make_msg;
+ }
+ goto done;
+ break;
+
+ case PW_NT_PASSWORD:
+ do_nt:
+ DEBUG("rlm_pap: Using NT encryption.");
+
+ normify(vp, 16);
+ if (vp->length != 16) {
+ DEBUG("rlm_pap: Configured NT-Password has incorrect length");
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured NT-Password has incorrect length");
+ goto make_msg;
+ }
+
+ sprintf(buff2,"%%{mschap:NT-Hash %s}",
+ request->password->strvalue);
+ if (!radius_xlat(digest,sizeof(digest),buff2,request,NULL)){
+ DEBUG("rlm_pap: mschap xlat failed");
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: mschap xlat failed");
+ goto make_msg;
+ }
+ if ((lrad_hex2bin(digest, digest, 16) != vp->length) ||
+ (memcmp(digest, vp->strvalue, vp->length) != 0)) {
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: NT password check failed");
+ goto make_msg;
+ }
+ goto done;
+ break;
+
+ case PW_LM_PASSWORD:
+ do_lm:
+ DEBUG("rlm_pap: Using LM encryption.");
+
+ normify(vp, 16);
+ if (vp->length != 16) {
+ DEBUG("rlm_pap: Configured LM-Password has incorrect length");
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured LM-Password has incorrect length");
+ goto make_msg;
+ }
+ sprintf(buff2,"%%{mschap:LM-Hash %s}",
+ request->password->strvalue);
+ if (!radius_xlat(digest,sizeof(digest),buff2,request,NULL)){
+ DEBUG("rlm_pap: mschap xlat failed");
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: mschap xlat failed");
+ goto make_msg;
+ }
+ if ((lrad_hex2bin(digest, digest, 16) != vp->length) ||
+ (memcmp(digest, vp->strvalue, vp->length) != 0)) {
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: LM password check failed");
+ make_msg:
+ DEBUG("rlm_pap: Passwords don't match");
+ module_fmsg_vp = pairmake("Module-Failure-Message",
+ module_fmsg, T_OP_EQ);
+ pairadd(&request->packet->vps, module_fmsg_vp);
+ return RLM_MODULE_REJECT;
+ }
+ goto done;
+ break;
+
+ case PAP_ENC_NS_MTA_MD5:
+ do_ns_mta_md5:
+ DEBUG("rlm_pap: Using NT-MTA-MD5 password");
+
+ if (vp->length != 64) {
+ DEBUG("rlm_pap: Configured NS-MTA-MD5-Password has incorrect length");
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured NS-MTA-MD5-Password has incorrect length");
+ goto make_msg;
+ }
+
+ /*
+ * Sanity check the value of NS-MTA-MD5-Password
+ */
+ if (lrad_hex2bin(vp->strvalue, buff, 32) != 16) {
+ DEBUG("rlm_pap: Configured NS-MTA-MD5-Password has invalid value");
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured NS-MTA-MD5-Password has invalid value");
+ goto make_msg;
+ }
+
+ /*
+ * Ensure we don't have buffer overflows.
+ *
+ * This really: sizeof(buff) - 2 - 2*32 - strlen(passwd)
+ */
+ if (strlen(request->password->strvalue) >= (sizeof(buff2) - 2 - 2 * 32)) {
+ DEBUG("rlm_pap: Configured password is too long");
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: password is too long");
+ goto make_msg;
+ }
+
+ /*
+ * Set up the algorithm.
+ */
+ {
+ char *p = buff2;
+
+ memcpy(p, &vp->strvalue[32], 32);
+ p += 32;
+ *(p++) = 89;
+ strcpy(p, request->password->strvalue);
+ p += strlen(p);
+ *(p++) = 247;
+ memcpy(p, &vp->strvalue[32], 32);
+ p += 32;
- PAP_INST_FREE(inst);
- return 0;
+ MD5Init(&md5_context);
+ MD5Update(&md5_context, buff2, p - buff2);
+ MD5Final(digest, &md5_context);
+ }
+ if (memcmp(digest, buff, 16) != 0) {
+ snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: NS-MTA-MD5 password check failed");
+ goto make_msg;
+ }
+ goto done;
+
+ default:
+ break;
+ }
+
+ DEBUG("rlm_pap: No password configured for the user. Cannot do authentication");
+ return RLM_MODULE_FAIL;
}
pap_instantiate, /* instantiation */
{
pap_authenticate, /* authentication */
- NULL, /* authorization */
+ pap_authorize, /* authorization */
NULL, /* preaccounting */
NULL, /* accounting */
NULL, /* checksimul */