modules/auth/api.c
5d7bdf0b
 /*
  * $Id$
  *
  * Digest Authentication Module
  *
95072403
  * Copyright (C) 2001-2003 FhG Fokus
5d7bdf0b
  *
  * This file is part of ser, a free SIP server.
  *
  * ser 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
  *
  * For a license to use the ser software under conditions
  * other than those described here, or to purchase support for this
  * software, please contact iptel.org by e-mail at the following addresses:
  *    info@iptel.org
  *
  * ser 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
  */
7c2afacc
 /*
  * History:
  * --------
  *  ...
  * 2008-07-01 set c->stale in auth_check_hdr_md5 (andrei)
  */
5d7bdf0b
 
 #include <string.h>
 #include "api.h"
 #include "../../dprint.h"
 #include "../../parser/digest/digest.h"
8477b7f3
 #include "../../sr_module.h"
8ee734e1
 #include "../../ut.h"
5d7bdf0b
 #include "auth_mod.h"
 #include "nonce.h"
 
b3f0a3f0
 static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth_body,
 		auth_result_t* auth_res);
1babd807
 
5d7bdf0b
 /*
  * Purpose of this function is to find credentials with given realm,
  * do sanity check, validate credential correctness and determine if
  * we should really authenticate (there must be no authentication for
  * ACK and CANCEL
1babd807
  * @param hdr output param where the Authorize headerfield will be returned.
  * @param check_hdr  pointer to the function checking Authorization header field
5d7bdf0b
  */
f6beb702
 auth_result_t pre_auth(struct sip_msg* msg, str* realm, hdr_types_t hftype,
b3f0a3f0
 						struct hdr_field**  hdr,
 						check_auth_hdr_t check_auth_hdr)
5d7bdf0b
 {
 	int ret;
 	auth_body_t* c;
1babd807
 	check_auth_hdr_t check_hf;
 	auth_result_t    auth_rv;
 
f6beb702
 	     /* ACK and CANCEL must be always authenticated, there is
5d7bdf0b
 	      * no way how to challenge ACK and CANCEL cannot be
 	      * challenged because it must have the same CSeq as
b3f0a3f0
 	      * the request to be canceled.
 	      * PRACK is also not authenticated
5d7bdf0b
 	      */
3a5d3392
 
b3f0a3f0
 	if (msg->REQ_METHOD & (METHOD_ACK|METHOD_CANCEL|METHOD_PRACK))
 		return AUTHENTICATED;
5d7bdf0b
 
 	     /* Try to find credentials with corresponding realm
 	      * in the message, parse them and return pointer to
 	      * parsed structure
 	      */
00d758fb
 	strip_realm(realm);
f6beb702
 	ret = find_credentials(msg, realm, hftype, hdr);
5d7bdf0b
 	if (ret < 0) {
f6beb702
 		LOG(L_ERR, "auth:pre_auth: Error while looking for credentials\n");
5d7bdf0b
 		return ERROR;
 	} else if (ret > 0) {
b3f0a3f0
 		DBG("auth:pre_auth: Credentials with realm '%.*s' not found\n",
 				realm->len, ZSW(realm->s));
5ec0e422
 		return NO_CREDENTIALS;
5d7bdf0b
 	}
 
 	     /* Pointer to the parsed credentials */
f6beb702
 	c = (auth_body_t*)((*hdr)->parsed);
5d7bdf0b
 
1babd807
 	    /* digest headers are in c->digest */
b3f0a3f0
 	DBG("auth: digest-algo: %.*s parsed value: %d\n",
 			c->digest.alg.alg_str.len, c->digest.alg.alg_str.s,
 			c->digest.alg.alg_parsed);
1babd807
 
451eb005
 	if (mark_authorized_cred(msg, *hdr) < 0) {
 		LOG(L_ERR, "auth:pre_auth: Error while marking parsed credentials\n");
 		return ERROR;
 	}
 
1babd807
 	    /* check authorization header field's validity */
 	if (check_auth_hdr == NULL) {
 		check_hf = auth_check_hdr_md5;
b3f0a3f0
 	} else {	/* use check function of external authentication module */
1babd807
 		check_hf = check_auth_hdr;
 	}
 	/* use the right function */
 	if (!check_hf(msg, c, &auth_rv)) {
 		return auth_rv;
 	}
 	
 	return DO_AUTHENTICATION;
 }
 
 /**
  * TODO move it to rfc2617.c 
  * 
  * @param auth_res return value of authentication. Maybe the it will be not affected.
  * @result if authentication should continue (1) or not (0)
  * 
  */
b3f0a3f0
 static int auth_check_hdr_md5(struct sip_msg* msg, auth_body_t* auth,
 		auth_result_t* auth_res)
1babd807
 {
 	int ret;
 	
 	    /* Check credentials correctness here */
 	if (check_dig_cred(&auth->digest) != E_DIG_OK) {
8ee734e1
 		LOG(L_ERR, "auth:pre_auth: Credentials are not filled properly\n");
1babd807
 		*auth_res = BAD_CREDENTIALS;
 		return 0;
5d7bdf0b
 	}
 
12690ddb
 	ret = check_nonce(auth, &secret1, &secret2, msg);
b3fde3a6
 	if (ret!=0){
7c2afacc
 		if (ret==3 || ret==4){
 			/* failed auth_extra_checks or stale */
1babd807
 			auth->stale=1; /* we mark the nonce as stale 
5ec0e422
 							(hack that makes our life much easier) */
 			*auth_res = STALE_NONCE;
 			return 0;
 		} else if (ret==6) {
 			*auth_res = NONCE_REUSED;
 			return 0;
1babd807
 		} else {
494b383e
 			DBG("auth:pre_auth: Invalid nonce value received (ret %d)\n", ret);
1babd807
 			*auth_res = NOT_AUTHENTICATED;
 			return 0;
b3fde3a6
 		}
5d7bdf0b
 	}
1babd807
 	return 1;
5d7bdf0b
 }
 
 /*
  * Purpose of this function is to do post authentication steps like
  * marking authorized credentials and so on.
  */
f6beb702
 auth_result_t post_auth(struct sip_msg* msg, struct hdr_field* hdr)
5d7bdf0b
 {
f6beb702
 	int res = AUTHENTICATED;
5d7bdf0b
 	auth_body_t* c;
 
f6beb702
 	c = (auth_body_t*)((hdr)->parsed);
5d7bdf0b
 
7c2afacc
 	if (c->stale ) {
f6beb702
 		if ((msg->REQ_METHOD == METHOD_ACK) || 
 		    (msg->REQ_METHOD == METHOD_CANCEL)) {
5d7bdf0b
 			     /* Method is ACK or CANCEL, we must accept stale
 			      * nonces because there is no way how to challenge
 			      * with new nonce (ACK has no response associated 
 			      * and CANCEL must have the same CSeq as the request 
95072403
 			      * to be canceled)
5d7bdf0b
 			      */
 		} else {
 			c->stale = 1;
f6beb702
 			res = NOT_AUTHENTICATED;
5d7bdf0b
 		}
 	}
aaf84de9
 
 	return res;
5d7bdf0b
 }
8477b7f3
 
b3f0a3f0
 /*
  * Calculate the response and compare with the given response string
  * Authorization is successful if this two strings are same
  */
 int auth_check_response(dig_cred_t* cred, str* method, char* ha1)
 {
 	HASHHEX resp, hent;
 
 	/*
 	 * First, we have to verify that the response received has
 	 * the same length as responses created by us
 	 */
 	if (cred->response.len != 32) {
 		DBG("check_response: Receive response len != 32\n");
9c2c336d
 		return BAD_CREDENTIALS;
b3f0a3f0
 	}
 
 	/*
 	 * Now, calculate our response from parameters received
 	 * from the user agent
 	 */
 	calc_response(ha1, &(cred->nonce),
 				  &(cred->nc), &(cred->cnonce),
 				  &(cred->qop.qop_str), cred->qop.qop_parsed == QOP_AUTHINT,
 				  method, &(cred->uri), hent, resp);
 
 	DBG("check_response: Our result = \'%s\'\n", resp);
 
 	/*
 	 * And simply compare the strings, the user is
 	 * authorized if they match
 	 */
 	if (!memcmp(resp, cred->response.s, 32)) {
 		DBG("check_response: Authorization is OK\n");
9c2c336d
 		return AUTHENTICATED;
b3f0a3f0
 	} else {
 		DBG("check_response: Authorization failed\n");
9c2c336d
 		return NOT_AUTHENTICATED;
b3f0a3f0
 	}
 }
 
8477b7f3
 
f93a3d56
 int bind_auth_s(auth_api_s_t* api)
8477b7f3
 {
 	if (!api) {
 		LOG(L_ERR, "bind_auth: Invalid parameter value\n");
 		return -1;
 	}
 
 	api->pre_auth = pre_auth;
 	api->post_auth = post_auth;
8ee734e1
 	api->build_challenge = build_challenge_hf;
b3f0a3f0
 	api->qop = &auth_qop;
1babd807
 	api->calc_HA1 = calc_HA1;
 	api->calc_response = calc_response;
b3f0a3f0
 	api->check_response = auth_check_response;
dc15caf8
 	api->auth_challenge = auth_challenge;
 	api->pv_authenticate = pv_authenticate;
 	api->consume_credentials = consume_credentials;
8477b7f3
 	return 0;
 }