Hello,
I would like to submit my code changes to PAM RADIUS authentication
module.
This code segment does user authorization (delivers user's privilege from
RADIUS server configuration database).
1. I introduced new flag service_type for PAM RADIUS configuration
file and attached sample configuration file.
2. _pam_parse routine checks if user authorization is required and sets
internal flag service_type while parse arguments
static int _pam_parse(int argc, const char **argv, radius_conf_t
*conf)
3. Add more RADIUS server response validations:
a. Check for the correct sequence number.
b. Check for the Access-Accept code value in response
packet
Argument for verify_packet has been changed
-verify_packet(char *secret, AUTH_HDR *response, unsigned char
*vector)
+verify_packet(char *secret, AUTH_HDR *response, AUTH_HDR *request)
4. Introduced routine to analyze RADIUS server response packet
+DecodeResponse(pam_handle_t *pamh, AUTH_HDR *response)
This routine parses response packet and searches for Service-Type
attribute in packet.
Value of Service-Type attribute RADIUS server reads from user's database
and delivers in response packet.
Value of Service-Type has been saved in pam environment variable
"Privilege" and is acceptable from calling application or any
other module/functions
Please, let me know if you need further explanation. How I can include my
changes in RADIUS module?
Best regards,
Leon
$ more sample.pam
#%PAM-1.0
#
# VALUE Service-Type
Login-User
1
# VALUE Service-Type
Framed-User
2
# VALUE Service-Type
Callback-Login-User 3
# VALUE Service-Type
Callback-Framed-User 4
# VALUE Service-Type
Outbound-User
5
# VALUE Service-Type
Administrative-User 6
# VALUE Service-Type
NAS-Prompt-User
7
# VALUE Service-Type
Authenticate-Only-User 8
# VALUE Service-Type
Callback-NAS-Prompt-User 9
auth required /lib/security/pam_radius_auth.so debug use_first_pass
retry=1 service_type
$ diff -u ../pam_radius/pam_radius_auth.c pam_radius_auth.c
@@ -46,6 +46,8 @@
static radius_server_t *live_server = NULL;
static time_t session_time;
+int service_type = 0;
+
/* logging */
static void _pam_log(int err, const char *format, ...)
{
@@ -84,6 +86,11 @@
} else if (!strcmp(*argv,
"skip_passwd")) {
ctrl |= PAM_SKIP_PASSWD;
+ } else if (!strcmp(*argv, "service_type"))
{
+ service_type = 1;
+
#ifdef RETRY
} else if (!strncmp(*argv,"retry=",6))
{
int i = atoi(*argv+6);
@@ -352,16 +359,110 @@
MD5Final(request->vector,
&my_md5); /* set the final vector
*/
}
+
+typedef struct {
+ unsigned char
cAttribute;
+ unsigned char cLength;
+ char
sValue[AUTH_PASS_LEN + 1];
+} RToken_t;
+
+#define
PW_SERVICE_TYPE
6 /* number */
+
+/*********************************************************
+* Function: DecodeResponse
+*
+* Description: Parses RADIUS server response attribute, sets user
privilege
+*
based on Service-Type configuration in RADIUS database
+*
+* Samples of configuration file /etc/raddb# more users
+* admin Password = "admin"
+* Service-Type =
Administrative-User
+* user Password = "user"
+* Service-Type =
NAS-Prompt-User
+*
+* Responses samples from RADIUS server:
+* Tue Mar 20 11:57:22 2001: [18482] Sending Accept of id 66 to
x.x.x.x
+* Tue Mar 20 11:57:22 2001: [18482] Service-Type =
Administrative-User
+*
+* Tue Mar 20 11:09:57 2001: [18482] Sending Accept of id 216 to
x.x.x.x
+* Tue Mar 20 11:09:57 2001: [18482] Service-Type =
NAS-Prompt-User
+*
+* Input: pam_handle_t *pamh - PAM structure handle
+* AUTH_HDR
*response - Response packet from RADIUS server
+* Output: none
+*
+* Algorithm:
+*
+*********************************************************/
+static void
+DecodeResponse(pam_handle_t *pamh, AUTH_HDR *response)
+{
+ char *pAttribute;
+ AUTH_HDR *pHeader = (AUTH_HDR
*) response;
+ RToken_t *pToken;
+ char sPrivilege[128];
+ int nRetval;
+
+ /* u_char pHeader->code; u_char
pHeader->id; u_short pHeader->length; */
+ for (pAttribute = (char *)
response + AUTH_VECTOR_LEN + sizeof(u_char) + sizeof(u_char) +
sizeof(u_short);
+
pAttribute < (char *) response + ntohs(pHeader->length);) {
+ pToken = (RToken_t *)
pAttribute;
+ if
(pToken->cAttribute == PW_SERVICE_TYPE) {
+ if
(service_type) { /* this option has been set in radius configuration file
*/
+
sprintf(sPrivilege, "Privilege=%d",
pToken->sValue[3]);
+
printf("sPrivilege %s\n", sPrivilege);
+
nRetval = pam_putenv(pamh, sPrivilege);
+
if (nRetval != PAM_SUCCESS) {
+
syslog(LOG_WARNING, "%s: unable to set privilege level for
PAM", __FUNCTION__);
+
printf("***pam_radius %s: unable to set privilege level for
PAM\n", __FUNCTION__);
+
}
+ } /* if
(service_type) */
+ }
+ pAttribute +=
pToken->cLength;
+ }
+} /* DecodeResponse */
+
/*
* Verify the response from the server
*/
static int
-verify_packet(char *secret, AUTH_HDR *response, unsigned char
*vector)
+verify_packet(char *secret, AUTH_HDR *response, AUTH_HDR *request)
{
MD5_CTX my_md5;
unsigned char
calculated[AUTH_VECTOR_LEN];
unsigned char
reply[AUTH_VECTOR_LEN];
+ unsigned char *vector;
+ vector = (unsigned char *) request->vector;
+
+ /*
+ * Check for the correct sequence number.
+ */
+
+ if (response->id != request->id) {
+ printf("***pam_radius %s: Bad message-id
received [%d/%d]\n", __FUNCTION__, request->id,
response->id);
+ return FALSE;
+ }
+
+ /*
+ * Check for the Access-Accept code value
+ */
+ if (response->code != PW_AUTHENTICATION_ACK) {
+ printf("***pam_radius %s: Bad code
[%d/%d]\n", __FUNCTION__, PW_AUTHENTICATION_ACK,
response->code);
+ return FALSE;
+ }
+
/*
* We could dispense with the memcpy, and do MD5's of
the packet
* + vector piece by piece. This is easier
understand, and maybe faster.
@@ -826,14 +953,15 @@
if
(conf->accounting_bug) {
p = "";
}
}
-
- if (!verify_packet(p,
response, request->vector)) {
+ if (!verify_packet(p,
response, request)) {
_pam_log(LOG_ERR, "packet from RADIUS server %s fails
verification",
server->hostname);
ok =
FALSE;
break;
}
@@ -1100,23 +1260,29 @@
/* Whew! Done the pasword checks, look for an authentication
acknowledge */
if (response->code == PW_AUTHENTICATION_ACK) {
+ DecodeResponse(pamh, response);
retval = PAM_SUCCESS;
} else {
#ifdef RETRY