Proposition fo digest authentication support in pam_radius module
Philippe Sultan
philippe.sultan at gmail.com
Mon Jul 25 18:33:16 CEST 2005
Hello everybody,
first, thank you for all the work that is done on freeradius!
I would like to propose a patch in order to use a the pam_radius
module as a radius client supporting digest authentication.
The provided patch applies to the pam_radius CVS version available at
this time (eg v 1.3.17). Here is a rough description :
- three files modified : pam_radius_auth.c, pam_radius_auth.h, radius.h
- added support for an argument 'digest' in the /etc/pam.d/client_app
configuration file, in order to use the feature
- two new structures added in pam_radius_auth.h : digest_attr and
digest_parameters
In digest authentication mode (activated with the 'digest' argument in
the corresponding pam.d configuration file), a call to the
conversation function (provided by the client application) is expected
to fill a digest_parameters structure with the necessary attributes.
The pam_radius module will parse the digest_parameters structure,
build a radius packet with all the Digest-Attributes and
Digest-Response, and send it to a RADIUS server.
A client application using this feature will have to be aware of these
structs as they are expected to be filled by the 'conversation'
function, for further processing by the pam_radius module.
This module could eventually be used by an application such as
Asterisk for SIP user authentication, as described in the
draft-sterman-aaa-sip-00.txt doc.
I successfully tested the module, along with a locally running
freeradius server in version 1.0.2, and a client application I wrote,
after I recorded a real SIP registration in order to grab two matching
Nonce and Digest-Response attributes. I can give an example of such
application if someone is interested.
Bye,
Philippe Sultan
INRIA
diff -urN pam_radius/pam_radius_auth.c pam_radius-digest_auth/pam_radius_auth.c
--- pam_radius/pam_radius_auth.c 2005-04-06 01:08:30.000000000 +0200
+++ pam_radius-digest_auth/pam_radius_auth.c 2005-07-25 17:46:13.000000000 +0200
@@ -140,7 +140,11 @@
ctrl |= PAM_DEBUG_ARG;
conf->debug = 1;
- } else {
+ } else if (!strcmp(*argv, "digest")) {
+ ctrl |= DIGEST_AUTH;
+ conf->authmethod = DIGEST_AUTH;
+ }
+ else {
_pam_log(LOG_WARNING, "unrecognized option '%s'", *argv);
}
}
@@ -473,6 +477,28 @@
}
/*
+ * Add a Digest-Attribute to a RADIUS packet.
+ */
+static void
+add_digest_attribute(AUTH_HDR *request, unsigned char subtype, CONST
unsigned char *subdata, int sublength)
+{
+ attribute_t *p;
+ attribute_t *sub;
+
+ p = (attribute_t *) ((unsigned char *)request + ntohs(request->length));
+ p->attribute = PW_DIGEST_ATTRIBUTES;
+ p->length = sublength + 4; /* the total size of the attribute */
+
+ sub = (attribute_t *)p->data;
+ sub->attribute = subtype;
+ sub->length = sublength + 2;
+
+ request->length = htons(ntohs(request->length) + p->length);
+ memcpy(sub->data, subdata, sublength);
+
+}
+
+/*
* Add an integer attribute to a RADIUS packet.
*/
static void
@@ -709,16 +735,23 @@
add_attribute(request, PW_USER_NAME, (unsigned char *) user, strlen(user));
/*
- * Add a password, if given.
+ * Add a password, if given. In case of digest authentication, the
+ * crendetials given by the application or user are added as a
+ * Digest-Response attribute.
*/
- if (password) {
- add_password(request, PW_PASSWORD, password, conf->server->secret);
-
- /*
- * Add a NULL password to non-accounting requests.
- */
- } else if (request->code != PW_ACCOUNTING_REQUEST) {
- add_password(request, PW_PASSWORD, "", conf->server->secret);
+ if (conf -> authmethod == DIGEST_AUTH) {
+ add_attribute(request, PW_DIGEST_RESPONSE, password, strlen(password));
+ }
+ else if (conf->authmethod == PASSWD_AUTH) {
+ if (password) {
+ add_password(request, PW_PASSWORD, password, conf->server->secret);
+
+ /*
+ * Add a NULL password to non-accounting requests.
+ */
+ } else if (request->code != PW_ACCOUNTING_REQUEST) {
+ add_password(request, PW_PASSWORD, "", conf->server->secret);
+ }
}
/* the packet is from localhost if on localhost, to make configs easier */
@@ -1037,6 +1070,38 @@
return PAM_SUCCESS;
}
+static int rad_digest_converse(pam_handle_t *pamh, int msg_style,
char *message, struct digest_parameters **params)
+{
+ CONST struct pam_conv *conv;
+ struct pam_message resp_msg;
+ CONST struct pam_message *msg[1];
+ struct pam_response *resp = NULL;
+ struct digest_parameters *aux;
+ int retval;
+
+ resp_msg.msg_style = msg_style;
+ resp_msg.msg = message;
+ msg[0] = &resp_msg;
+
+ /* grab the digest response and the digest attributes */
+ retval = pam_get_item(pamh, PAM_CONV, (CONST void **) &conv);
+ PAM_FAIL_CHECK;
+
+ retval = conv->conv(1, msg, &resp,conv->appdata_ptr);
+ PAM_FAIL_CHECK;
+
+ aux = (struct digest_parameters *)resp->resp;
+
+ *params = (struct digest_parameters *)malloc(sizeof(struct
digest_parameters));
+
+ /* copy the content of appdata_ptr into the params structure */
+ memcpy(*params, resp->resp, sizeof(struct digest_parameters));
+
+ free(resp);
+
+ return PAM_SUCCESS;
+}
+
/**************************************************************************
* GENERAL CODE
**************************************************************************/
@@ -1054,6 +1119,7 @@
{
CONST char *user;
char *password = NULL;
+ struct digest_parameters *d_params;
CONST char *rhost;
char *resp2challenge = NULL;
int ctrl;
@@ -1066,6 +1132,10 @@
radius_conf_t config;
int tries;
+ /* sets the authentication method to AUTH_PASSWD by default. Can be modified
+ in the configuration file */
+ config.authmethod = PASSWD_AUTH;
+
ctrl = _pam_parse(argc, argv, &config);
tries = ((ctrl & PAM_RETRY) >> 4) + 1;
@@ -1126,16 +1196,28 @@
retval = PAM_AUTH_ERR; /* use one pass only, stopping if it fails */
goto error;
}
-
+
+ /* which authentication method are we using? */
+ if (ctrl & DIGEST_AUTH) {
+ retval = rad_digest_converse(pamh, PAM_PROMPT_ECHO_OFF,
"Digest: ", &d_params);
+ PAM_FAIL_CHECK;
+ }
+
/* check to see if we send a NULL password the first time around */
- if (!(ctrl & PAM_SKIP_PASSWD)) {
+ else if (!(ctrl & PAM_SKIP_PASSWD)) {
retval = rad_converse(pamh, PAM_PROMPT_ECHO_OFF, "Password: ",
&password);
PAM_FAIL_CHECK;
}
} /* end of password == NULL */
- build_radius_packet(request, user, password, &config);
+ /* for digest authentication, we give the digest response from
+ the application as credentials */
+ if (ctrl & DIGEST_AUTH) {
+ build_radius_packet(request, user, d_params->digest_response, &config);
+ }
+ else
+ build_radius_packet(request, user, password, &config);
/* not all servers understand this service type, but some do */
add_int_attribute(request, PW_USER_SERVICE_TYPE, PW_AUTHENTICATE_ONLY);
@@ -1152,6 +1234,30 @@
strlen(rhost));
}
+ /*
+ * In case of digest authentication, build the necessary Digest-Attributes
+ * by parsing the d_params->d_attrs_list table.
+ */
+ if (ctrl & DIGEST_AUTH) {
+ int i;
+ digest_attr_t *aux = d_params->d_attrs_list;
+ DPRINT(LOG_DEBUG, "Digest response : %s",
+ d_params->digest_response);
+ DPRINT(LOG_DEBUG, "number of digest attributes = %d",
d_params->num_attrs);
+ for (i=0; i < d_params->num_attrs; i++) {
+ if (i >= MAX_DIGEST_ATTRS) {
+ DPRINT(LOG_DEBUG, "Number of maximum digest attributes (%d)
exceeded. Ignoring new ones", MAX_DIGEST_ATTRS);
+ break;
+ }
+ DPRINT(LOG_DEBUG, "Type = %d", (int)aux->type);
+ DPRINT(LOG_DEBUG, "Length = %d", (int)aux->length);
+ DPRINT(LOG_DEBUG, "Value = %s", aux->value);
+ add_digest_attribute(request, aux->type, aux->value, strlen(aux->value));
+ aux ++;
+ }
+
+ }
+
DPRINT(LOG_DEBUG, "Sending RADIUS request code %d", request->code);
retval = talk_radius(&config, request, response, password, NULL, tries);
diff -urN pam_radius/pam_radius_auth.h pam_radius-digest_auth/pam_radius_auth.h
--- pam_radius/pam_radius_auth.h 2003-09-19 16:41:32.000000000 +0200
+++ pam_radius-digest_auth/pam_radius_auth.h 2005-07-25 16:22:23.000000000 +0200
@@ -51,8 +51,23 @@
int accounting_bug;
int sockfd;
int debug;
+ int authmethod;
} radius_conf_t;
+typedef struct digest_attr {
+ char type;
+ char length;
+ char value[256];
+} digest_attr_t;
+
+#define MAX_DIGEST_ATTRS 10
+
+struct digest_parameters {
+ char *digest_response;
+ int num_attrs;
+ digest_attr_t d_attrs_list[MAX_DIGEST_ATTRS];
+};
+
/*************************************************************************
* Platform specific defines
@@ -83,6 +98,9 @@
#define PAM_USE_FIRST_PASS 4
#define PAM_TRY_FIRST_PASS 8
+#define PASSWD_AUTH 16
+#define DIGEST_AUTH 32
+
#define PAM_RETRY 0x30
/* Module defines */
diff -urN pam_radius/radius.h pam_radius-digest_auth/radius.h
--- pam_radius/radius.h 2000-08-22 17:24:47.000000000 +0200
+++ pam_radius-digest_auth/radius.h 2005-07-25 16:22:30.000000000 +0200
@@ -125,6 +125,21 @@
#define PW_LOGIN_LAT_PORT 63 /* string */
#define PW_PROMPT 64 /* integer */
+/* Experimental SIP-specific attributes (draft-sterman-aaa-sip-00.txt etc) */
+
+#define PW_DIGEST_RESPONSE 206 /* string */
+#define PW_DIGEST_ATTRIBUTES 207 /* string */
+#define PW_DIGEST_REALM 1 /* string */
+#define PW_DIGEST_NONCE 2 /* string */
+#define PW_DIGEST_METHOD 3 /* string */
+#define PW_DIGEST_URI 4 /* string */
+#define PW_DIGEST_QOP 5 /* string */
+#define PW_DIGEST_ALGORITHM 6 /* string */
+#define PW_DIGEST_BODY_DIGEST 7 /* string */
+#define PW_DIGEST_CNONCE 8 /* string */
+#define PW_DIGEST_NONCE_COUNT 9 /* string */
+#define PW_DIGEST_USER_NAME 10 /* string */
+
/*
* INTEGER TRANSLATIONS
*/
More information about the Freeradius-Devel
mailing list