Allan:
Allow me to intrude on this, since this is related to a question I wanted to ask.
I used previously FreeRadius version 0.9.1. To be able to use Digest-MD5 authentication, I needed to change the file "rlm_digest.c" in directory:
~/freeradius-0.9.1/src/modules/rlm_digest
The file was replaced by a patch file a colleague obtained from Michael Griego, who published the patch. Curently I'm using FreeRadius version 1.0.1, and I was expecting to find the code from the patch there, but it wasn't. So I added the patch file again. Without that patch file, Digest-MD5 authentication doesn't work.
My question is: why this patch file has not been added yet to FreeRadius, since its really needed ?
Thanks in advance,
Antonio Martinez
P.S.: For clarity, I have attached the patch file. Maybe Michael G. has a say on this.
--On Wednesday, November 24, 2004 09:17:02 AM -0500 Alan DeKok <[EMAIL PROTECTED]> wrote:
prabhan <[EMAIL PROTECTED]> wrote:Does FreeRadius version 0.9.1 support draft-sterman-aaa-sip-00, an extension to RADIUS for Digest authentication.
Did you look in radiusd.conf? The word "digest" appears there.
See also "doc/rfc/draft-sterman-aaa-sip-00.txt".
The information you're looking for is already in the server.
Alan DeKok.
- List info/subscribe/unsubscribe? See http://www.freeradius.org/list/users.html
/* * rlm_chap.c * * Version: $Id: rlm_digest.c,v 1.5 2004/05/15 14:57:41 mgriego Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright 2002 The FreeRADIUS server project * Copyright 2002 Alan DeKok <[EMAIL PROTECTED]> */
#include "autoconf.h" #include "libradius.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include "radiusd.h" #include "modules.h" #include "conffile.h" #include "rad_assert.h" #include "md5.h" static const char rcsid[] = "$Id: rlm_digest.c,v 1.5 2004/05/15 14:57:41 mgriego Exp $"; static int digest_authorize(void *instance, REQUEST *request) { VALUE_PAIR *vp; /* quiet the compiler */ instance = instance; /* * We need both of these attributes to do the authentication. */ vp = pairfind(request->packet->vps, PW_DIGEST_RESPONSE); if (vp == NULL) { return RLM_MODULE_NOOP; } /* * Check the sanity of the attribute. */ if (vp->length != 32) { DEBUG("ERROR: Received invalid Digest-Response attribute (length %d should be 32)", vp->length); return RLM_MODULE_INVALID; } /* * We need these, too. */ vp = pairfind(request->packet->vps, PW_DIGEST_ATTRIBUTES); if (vp == NULL) { DEBUG("ERROR: Received Digest-Response without Digest-Attributes"); return RLM_MODULE_INVALID; } /* * Loop through the Digest-Attributes, sanity checking them. */ DEBUG(" rlm_digest: Converting Digest-Attributes to something sane..."); while (vp) { int length = vp->length; int attrlen; uint8_t *p = &vp->strvalue[0]; VALUE_PAIR *sub; /* * Until this stupidly encoded attribure is exhausted. */ while (length > 0) { /* * The attribute type must be valid */ if ((p[0] == 0) || (p[0] > 10)) { DEBUG("ERROR: Received Digest-Attributes with invalid sub-attribute %d", p[0]); return RLM_MODULE_INVALID; } attrlen = p[1]; /* stupid VSA format */ /* * Too short. */ if (attrlen < 3) { DEBUG("ERROR: Received Digest-Attributes with short sub-attribute %d, of length %d", p[0], attrlen); return RLM_MODULE_INVALID; } /* * Too long. */ if (attrlen > length) { DEBUG("ERROR: Received Digest-Attributes with long sub-attribute %d, of length %d", p[0], attrlen); return RLM_MODULE_INVALID; } /* * Create a new attribute, broken out of * the stupid sub-attribute crap. * * Didn't they know that VSA's exist? */ sub = paircreate(PW_DIGEST_REALM - 1 + p[0], PW_TYPE_STRING); if (!sub) { return RLM_MODULE_FAIL; /* out of memory */ } memcpy(&sub->strvalue[0], &p[2], attrlen - 2); sub->strvalue[attrlen - 2] = '\0'; sub->length = attrlen - 2; if (debug_flag) { putchar('\t'); vp_print(stdout, sub); putchar('\n'); } /* * And add it to the request pairs. */ pairadd(&request->packet->vps, sub); //pairadd(&request->packet->vps, sub); /* * FIXME: Check for the existence * of the necessary attributes! */ length -= attrlen; p += attrlen; } /* loop over this one attribute */ /* * Find the next one, if it exists. */ vp = pairfind(vp->next, PW_DIGEST_ATTRIBUTES); } /* * Everything's OK, add a digest authentication type. */ if (pairfind(request->config_items, PW_AUTHTYPE) == NULL) { DEBUG("rlm_digest: Adding Auth-Type = DIGEST"); pairadd(&request->config_items, pairmake("Auth-Type", "DIGEST", T_OP_EQ)); } #if 0 DEBUG("rlm_digest: Adding Digest-In reply!! = DIGEST"); pairadd(&request->reply->vps, pairmake("Digest-Response", "TESTING", T_OP_EQ)); #endif return RLM_MODULE_OK; } /* * Convert a string in hex to one in binary */ static void hex2bin(uint8_t *out, const uint8_t *in) { unsigned int tmp; while (*in) { sscanf(in, "%02x", &tmp); *out = tmp; out++; in += 2; } } char *md5(char *string) { MD5_CTX context; unsigned char digest[16]; static char output[33]; int i; MD5Init(&context); MD5Update(&context, string, strlen(string)); MD5Final(digest, &context); memset(output, 0, 33); for (i = 0; i < 16; i++) sprintf(output + (i * 2), "%02x", ((unsigned char) digest[i])); return output; } #define HEX_AUTH_MD5_STR_SIZE 33 #define HEX_STR_TSP_PROTOCOL_AUTHENTICATE "AUTHENTICATE" /* * Perform all of the wondrous variants of digest authentication. */ static int digest_authenticate(void *instance, REQUEST *request) { int i, len; int a1_len, a2_len, kd_len; char temp_nonce[33]; unsigned long unonce; uint8_t a1[(MAX_STRING_LEN + 1) * 5]; /* can be 5 attributes */ uint8_t a2[(MAX_STRING_LEN + 1) * 3]; /* can be 3 attributes */ uint8_t kd[(MAX_STRING_LEN + 1) * 5]; uint8_t kd1[(MAX_STRING_LEN + 1) * 5]; uint8_t hash[16]; /* MD5 output */ uint8_t hash1[16]; /* MD5 output */ VALUE_PAIR *vp; VALUE_PAIR *qop, *nonce, *cnonce, *noncecount; char final_res_str[HEX_AUTH_MD5_STR_SIZE]; char cA2[HEX_AUTH_MD5_STR_SIZE]; char *A2Fmt = "%s:%s",*String, *ChallRespFmt = "%s:%s:00000001:%lu:%s:%s"; char *A1Fmt = ":%s:%lu", *A11, A1[33]; /* * We require access to the plain-text password. */ vp = pairfind(request->config_items, PW_PASSWORD); if (!vp) { radlog(L_AUTH, "rlm_digest: Configuration item \"User-Password\" is required for authentication."); return RLM_MODULE_INVALID; } /* * We require access to the Digest-Nonce-Value */ nonce = pairfind(request->packet->vps, PW_DIGEST_NONCE); if (!nonce) { DEBUG("ERROR: No Digest-Nonce: Cannot perform Digest authentication"); return RLM_MODULE_INVALID; } /* * A1 = Digest-User-Name ":" Realm ":" Password */ vp = pairfind(request->packet->vps, PW_DIGEST_USER_NAME); if (!vp) { DEBUG("ERROR: No Digest-User-Name: Cannot perform Digest authentication"); return RLM_MODULE_INVALID; } memcpy(&a1[0], &vp->strvalue[0], vp->length); a1_len = vp->length; a1[a1_len] = ':'; a1_len++; vp = pairfind(request->packet->vps, PW_DIGEST_REALM); if (!vp) { DEBUG("ERROR: No Digest-Realm: Cannot perform Digest authentication"); return RLM_MODULE_INVALID; } memcpy(&a1[a1_len], &vp->strvalue[0], vp->length); a1_len += vp->length; a1[a1_len] = ':'; a1_len++; vp = pairfind(request->config_items, PW_PASSWORD); if (!vp) { DEBUG("ERROR: No User-Password: Cannot perform Digest authentication"); return RLM_MODULE_INVALID; } memcpy(&a1[a1_len], &vp->strvalue[0], vp->length); a1_len += vp->length; a1[a1_len] = '\0'; DEBUG2("A1 = %s", a1); DEBUG2("a1_len = %d", a1_len); A11 = (char *) malloc(a1_len); memset(A11, 0, a1_len); memcpy(&A11[0], &a1[0], a1_len); DEBUG2("sending A11 = %s", A11); strncpy(A1, md5(A11), HEX_AUTH_MD5_STR_SIZE); DEBUG2("sending A1 = %s", A1); free(A11); /* * See which variant we calculate. */ vp = pairfind(request->packet->vps, PW_DIGEST_ALGORITHM); if ((vp != NULL) && (strcasecmp(vp->strvalue, "MD5-sess") == 0)) { librad_md5_calc(hash, &a1[0], a1_len); memcpy(&a1[0], hash, 16); a1_len = 16; a1[a1_len] = ':'; a1_len++; memcpy(&a1[a1_len], &nonce->strvalue[0], nonce->length); /* memcpy(&temp_nonce[a1_len], &nonce->strvalue[0], nonce->length);*/ /* unonce = strtoul(&nonce->strvalue[0], temp_nonce, 16);*/ a1_len += nonce->length; a1[a1_len] = ':'; a1_len++; vp = pairfind(request->packet->vps, PW_DIGEST_CNONCE); memcpy(&a1[a1_len], &vp->strvalue[0], vp->length); a1_len += vp->length; /* * Tack on the Digest-Nonce */ #if 0 hex2bin(&a1[a1_len], &nonce->strvalue[0]); a1_len += (nonce->length >> 1); /* FIXME: CHECK LENGTH */ a1[a1_len] = ':'; a1_len++; vp = pairfind(request->packet->vps, PW_DIGEST_CNONCE); if (!vp) { DEBUG("ERROR: No Digest-CNonce: Cannot perform Digest authentication"); return RLM_MODULE_INVALID; } hex2bin(&a1[a1_len], &vp->strvalue[0]); a1_len += (vp->length >> 1); /* FIXME: CHECK LENGTH */ #endif DEBUG2("A1 = %s", a1); } else if ((vp != NULL) && (strcasecmp(vp->strvalue, "MD5") != 0)) { /* * We check for "MD5-sess" and "MD5". * Anything else is an error. */ DEBUG("ERROR: Unknown Digest-Algorithm \"%s\": Cannot perform Digest authentication", vp->strvalue); return RLM_MODULE_INVALID; } /* * A2 = Digest-Method ":" Digest-URI */ vp = pairfind(request->packet->vps, PW_DIGEST_METHOD); if (!vp) { DEBUG("ERROR: No Digest-Method: Cannot perform Digest authentication"); return RLM_MODULE_INVALID; } memcpy(&a2[0], &vp->strvalue[0], vp->length); a2_len = vp->length; a2[a2_len] = ':'; a2_len++; vp = pairfind(request->packet->vps, PW_DIGEST_URI); if (!vp) { DEBUG("ERROR: No Digest-URI: Cannot perform Digest authentication"); return RLM_MODULE_INVALID; } memcpy(&a2[a2_len], &vp->strvalue[0], vp->length); a2_len += vp->length; DEBUG("rlm_digest: Beginging of adventure.."); memset(cA2, 0, sizeof(cA2)); len = strlen(A2Fmt) + strlen(HEX_STR_TSP_PROTOCOL_AUTHENTICATE) + vp->length + 1 ; if ((String = malloc(len)) == NULL) { exit(0); } snprintf(String, len, A2Fmt, "", &vp->strvalue[0]); strncpy(cA2, String, HEX_AUTH_MD5_STR_SIZE); free(String); DEBUG2("cA2 = %s", cA2); #if 0 cnonce = pairfind(request->packet->vps, PW_DIGEST_NONCE_COUNT); DEBUG2("cnonce->length = %d", cnonce->length); DEBUG2("cnonce->strvalue = %s", cnonce->strvalue); noncecount = pairfind(request->packet->vps, PW_DIGEST_NONCE_COUNT); DEBUG2("noncecount->length = %d", noncecount->length); DEBUG2("noncecount->strvalue = %s", noncecount->strvalue); qop = pairfind(request->packet->vps, PW_DIGEST_QOP); DEBUG2("qop->length = %d", qop->length); DEBUG2("qop->strvalue = %s", qop->strvalue); len = strlen(ChallRespFmt) + 32 /* md5(A1) */ + nonce->length + 16 /*cnonce->length*/ + qop->length /*strlen("auth")*/ + 32 /* md5(A2) */ + 1 /* \n */; DEBUG2("len = %d", len); if ((String = malloc(len)) == NULL) { exit(0); } unonce = strtoul(&cnonce->strvalue[0], temp_nonce, 16); DEBUG2("unonce- = %lu", unonce); /* *ChallRespFmt = "%s:%s:00000001:%lu:%s:%s";*/ printf(ChallRespFmt, A1, &nonce->strvalue[0], unonce, qop->strvalue, cA2); snprintf(String, len, ChallRespFmt, a1, &nonce->strvalue[0], unonce, qop->strvalue, cA2); strncpy(final_res_str, md5(String), HEX_AUTH_MD5_STR_SIZE); free(String); DEBUG2("final str = %s", final_res_str); DEBUG("rlm_digest: Adding Digest-In reply!! = DIGEST"); pairadd(&request->reply->vps, pairmake("Digest-Response",final_res_str , T_OP_EQ)); #endif /* * QOP is "auth-int", tack on ": Digest-Body-Digest" */ qop = pairfind(request->packet->vps, PW_DIGEST_QOP); if ((qop != NULL) && (strcasecmp(qop->strvalue, "auth-int") == 0)) { VALUE_PAIR *body; /* * Add in Digest-Body-Digest */ a2[a2_len] = ':'; a2_len++; /* * Must be a hex representation of an MD5 digest. */ body = pairfind(request->packet->vps, PW_DIGEST_BODY_DIGEST); if (!body) { DEBUG("ERROR: No Digest-Body-Digest: Cannot perform Digest authentication"); return RLM_MODULE_INVALID; } rad_assert(body->length == 32); /* FIXME: check in 'auth' */ hex2bin(&a2[a2_len], &body->strvalue[0]); a2_len += (body->length >> 1); } else if ((qop != NULL) && (strcasecmp(qop->strvalue, "auth") != 0)) { DEBUG("ERROR: Unknown Digest-QOP \"%s\": Cannot perform Digest authentication", qop->strvalue); return RLM_MODULE_INVALID; } a2[a2_len] = '\0'; DEBUG2("A2 = %s", a2); /* * KD = H(A1) : Digest-Nonce ... : H(A2) */ librad_md5_calc(&hash[0], &a1[0], a1_len); for (i = 0; i < 16; i++) { sprintf(&kd[i * 2], "%02x", hash[i]); } #ifndef NDEBUG if (debug_flag) { printf("H(A1) = "); for (i = 0; i < 16; i++) { printf("%02x", hash[i]); } printf("\n"); } #endif kd_len = 32; kd[kd_len] = ':'; kd_len++; memcpy(&kd[kd_len], nonce->strvalue, nonce->length); kd_len += nonce->length; /* * No QOP defined. Do RFC 2069 compatibility. */ if (!qop) { /* * Do nothing here. */ } else { /* Digest-QOP MUST be "auth" or "auth-int" */ /* * Tack on ":" Digest-Nonce-Count ":" Digest-CNonce * ":" Digest-QOP */ kd[kd_len] = ':'; kd_len++; vp = pairfind(request->packet->vps, PW_DIGEST_NONCE_COUNT); if (!vp) { DEBUG("ERROR: No Digest-Nonce-Count: Cannot perform Digest authentication"); return RLM_MODULE_INVALID; } memcpy(&kd[kd_len], &vp->strvalue[0], vp->length); kd_len += vp->length; kd[kd_len] = ':'; kd_len++; vp = pairfind(request->packet->vps, PW_DIGEST_CNONCE); if (!vp) { DEBUG("ERROR: No Digest-CNonce: Cannot perform Digest authentication"); return RLM_MODULE_INVALID; } memcpy(&kd[kd_len], &vp->strvalue[0], vp->length); kd_len += vp->length; kd[kd_len] = ':'; kd_len++; memcpy(&kd[kd_len], &qop->strvalue[0], qop->length); kd_len += qop->length; } /* * Tack on ":" H(A2) */ kd[kd_len] = ':'; #if 1 memcpy(kd1, kd, kd_len); kd1[kd_len] = ':'; librad_md5_calc(&hash1[0], &cA2[0], strlen(cA2)); kd_len++; for (i = 0; i < 16; i++) { sprintf(&kd1[kd_len + (i * 2)], "%02x", hash1[i]); } DEBUG2("KD1 = %s\n", &kd1[0]); memset(final_res_str, 0, sizeof(final_res_str)); strncpy(final_res_str, md5(kd1), HEX_AUTH_MD5_STR_SIZE); DEBUG2("final str = %s", final_res_str); #endif librad_md5_calc(&hash[0], &a2[0], a2_len); for (i = 0; i < 16; i++) { sprintf(&kd[kd_len + (i * 2)], "%02x", hash[i]); } #ifndef NDEBUG if (debug_flag) { printf("H(A2) = "); for (i = 0; i < 16; i++) { printf("%02x", hash[i]); } printf("\n"); } #endif kd_len += 32; kd[kd_len] = 0; DEBUG2("KD = %s\n", &kd[0]); kd1[kd_len] = 0; DEBUG2("again KD1 = %s\n", &kd1[0]); /* * Take the hash of KD. */ librad_md5_calc(&hash[0], &kd[0], kd_len); memcpy(&kd[0], &hash[0], 16); librad_md5_calc(&hash1[0], &kd1[0], kd_len); memcpy(&kd1[0], &hash1[0], 16); DEBUG("rlm_digest: Adding Digest-In reply!! = DIGEST"); pairadd(&request->reply->vps, pairmake("Digest-Response",final_res_str , T_OP_EQ)); /* * Get the binary value of Digest-Response */ vp = pairfind(request->packet->vps, PW_DIGEST_RESPONSE); rad_assert(vp != NULL); hex2bin(&hash[0], &vp->strvalue[0]); #ifndef NDEBUG if (debug_flag) { printf("EXPECTED "); for (i = 0; i < 16; i++) { printf("%02x", kd[i]); } printf("\n"); printf("RECEIVED "); for (i = 0; i < 16; i++) { printf("%02x", hash[i]); } printf("\n"); } #endif /* * And finally, compare the digest in the packet with KD. */ if (memcmp(&kd[0], &hash[0], 16) == 0) { return RLM_MODULE_OK; } DEBUG("rlm_digest: FAILED authentication"); return RLM_MODULE_REJECT; } /* * The module name should be the only globally exported symbol. * That is, everything else should be 'static'. * * If the module needs to temporarily modify it's instantiation * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE. * The server will then take care of ensuring that the module * is single-threaded. */ module_t rlm_digest = { "DIGEST", 0, /* type */ NULL, /* initialization */ NULL, /* instantiation */ { digest_authenticate, /* authentication */ digest_authorize, /* authorization */ NULL, /* preaccounting */ NULL, /* accounting */ NULL, /* checksimul */ NULL, /* pre-proxy */ NULL, /* post-proxy */ NULL /* post-auth */ }, NULL, /* detach */ NULL, /* destroy */ };