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