Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 62036) +++ channels/chan_sip.c (working copy) @@ -8726,7 +8726,7 @@ ast_copy_string(aux.value, keys[K_USER].s, sizeof(aux.value)); ast_push_digest_attribute(¶ms, &aux); - retval = ast_digest_authenticate(user, ¶ms); + retval = ast_radius_digest_authenticate(user, ¶ms); return retval; } Index: include/asterisk/auth.h =================================================================== --- include/asterisk/auth.h (revision 62036) +++ include/asterisk/auth.h (working copy) @@ -50,14 +50,15 @@ } digest_attr_t; struct digest_parameters { - char digest_response[32]; + char digest_response[33]; int num_attrs; digest_attr_t d_attrs_list[MAX_DIGEST_ATTRS]; }; +int ast_ldap_authenticate(const char*, const char*); int ast_push_digest_attribute(struct digest_parameters* , digest_attr_t*); +int ast_radius_digest_authenticate(const char*, struct digest_parameters *); +int ast_radius_authenticate(const char*, const char*); -int ast_digest_authenticate(const char*, struct digest_parameters *); - #endif /* HAVE_RADIUS */ Index: main/manager.c =================================================================== --- main/manager.c (revision 62036) +++ main/manager.c (working copy) @@ -78,6 +78,7 @@ #include "asterisk/http.h" #include "asterisk/threadstorage.h" #include "asterisk/linkedlists.h" +#include "asterisk/auth.h" /*! * Linked list of events. @@ -884,6 +885,8 @@ struct ast_ha *ha = NULL; char *password = NULL; int readperm = 0, writeperm = 0; + char pwd[256]; /* used by res_auth */ + struct ast_flags res = {0}; /* used by res_auth */ if (ast_strlen_zero(user)) /* missing username */ return -1; @@ -1003,8 +1006,38 @@ } } else if (password) { const char *pass = astman_get_header(m, "Secret"); - if (!strcmp(password, pass)) - error = 0; + int tmp; + + res.flags = ast_parse_secret(user, password, pwd); + + if (ast_test_flag(&res, PW_SET)) { + /* Password has been retrieved from config file, + or from an external source. Let's now process it */ + if (ast_test_flag(&res, PW_FILE)) { + ast_log(LOG_DEBUG, "Password retrieved from file for user '%s' -- password '%s'\n", user, pwd); + } else if (ast_test_flag(&res, PW_LDAP)) { + ast_log(LOG_DEBUG, "Password retrieved from LDAP for user '%s' -- password '%s'\n", user, pwd); + } + if (!strcmp(pwd, pass)) + error = 0; + } else if (ast_test_flag(&res, AUTH_LDAP)) { + /* Authenticate using LDAP bind */ + tmp = ast_ldap_authenticate(user, pass); + ast_log(LOG_DEBUG, "LDAP authentication %s\n", tmp >= 0 ? "SUCCEEDED" : "FAILED"); + if (tmp >= 0) + error = 0; + } else if (ast_test_flag(&res, AUTH_RADIUS)) { + /* Authenticate using RADIUS (simple authentication) */ + tmp = ast_radius_authenticate(user, pass); + ast_log(LOG_DEBUG, "RADIUS authentication %s\n", tmp >= 0 ? "SUCCEEDED" : "FAILED"); + if (tmp >= 0) + error = 0; + } else { + /* secret has an old format */ + if (!strcmp(password, pass)) + error = 0; + } + } if (error) { ast_log(LOG_NOTICE, "%s failed to authenticate as '%s'\n", ast_inet_ntoa(s->sin.sin_addr), user); Index: res/res_auth.c =================================================================== --- res/res_auth.c (revision 62036) +++ res/res_auth.c (working copy) @@ -63,12 +63,13 @@ static char username_attr[128] = ""; static char userpw_attr [128] = ""; static int get_ldap_password(const char *, char *); - +static int ldap_connect(void); +static int ldap_disconnect(void); +static int ldap_reconnect(void); #endif /* HAVE_OPENLDAP */ #ifdef HAVE_RADIUS -AST_MUTEX_DEFINE_STATIC(rc_lock); #define RC_CONFIG_FILE "/usr/local/etc/radiusclient-ng/radiusclient.conf" static rc_handle *rh = NULL; #endif /* HAVE_RADIUS */ @@ -229,7 +230,7 @@ int bind_result = 0; if (ldapConn) { - ast_log(LOG_NOTICE, "Already connected.\n"); + ast_log(LOG_DEBUG, "Already connected.\n"); return 1; } @@ -244,15 +245,15 @@ } if (dbuser && *dbuser) { - ast_log(LOG_NOTICE, "Binding to %s as %s\n", dbhost, dbuser); + ast_log(LOG_DEBUG, "Binding to %s as %s\n", dbhost, dbuser); bind_result = ldap_simple_bind_s(ldapConn, dbuser, dbpass); } else { - ast_log(LOG_NOTICE, "Binding anonymously to %s\n", dbhost); + ast_log(LOG_DEBUG, "Binding anonymously to %s\n", dbhost); bind_result = ldap_simple_bind_s(ldapConn, NULL, NULL); } if (bind_result == LDAP_SUCCESS) { - ast_log(LOG_NOTICE, "Successfully connected to database.\n"); + ast_log(LOG_DEBUG, "Successfully connected to database.\n"); return 0; } else { ast_log(LOG_WARNING, "Bind failed: %s\n", ldap_err2string(bind_result)); @@ -269,12 +270,23 @@ { ldap_unbind(ldapConn); ldapConn = NULL; + return 1; +} - return 1; - +/*!\brief Reconnects to the LDAP server +*/ +static int ldap_reconnect(void) +{ + int res; + + ast_log(LOG_DEBUG, "Reconnecting to LDAP server\n"); + ldap_disconnect(); + res = ldap_connect(); + + return res; } -/*!\brief A function that finds a user password from an LDAP database based +/*!\brief A function to retrieve a user password from an LDAP database based on the LDAP filter set in the auth.conf file \param username the username which password is seeked @@ -287,6 +299,7 @@ { int result = 0; LDAPMessage *ldap_result = NULL; + LDAPMessage *ldap_entry = NULL; char ldap_filter[128]; int num_entry = 0; char **values = NULL; @@ -297,9 +310,25 @@ LDAP_SCOPE_SUBTREE, ldap_filter, NULL, 0, &ldap_result); - if (result < 0) { - ast_log(LOG_ERROR, "LDAP search failed.\n"); - return -1; + /* some servers may close the connection unexpectedly, so let's + try to reconnect and start a new search operation */ + if (result != LDAP_SUCCESS) { + switch (result) { + case LDAP_SERVER_DOWN: + case LDAP_BUSY: + case LDAP_UNAVAILABLE: + if (ldap_reconnect() < 0) { + ast_log(LOG_ERROR, "Lost connection to LDAP server\n"); + return -1; + } else + result = ldap_search_s(ldapConn, dbbasedn, + LDAP_SCOPE_SUBTREE, ldap_filter, NULL, 0, + &ldap_result); + break; + default: + ast_log(LOG_ERROR, "LDAP search failed : %s\n", ldap_err2string(result)); + return -1; + } } num_entry = ldap_count_entries(ldapConn, ldap_result); @@ -310,7 +339,6 @@ } /* only process the first retrieved entry */ - LDAPMessage *ldap_entry = NULL; ldap_entry = ldap_first_entry(ldapConn, ldap_result); values = ldap_get_values(ldapConn, ldap_entry, userpw_attr); @@ -330,6 +358,92 @@ return result; } + +/*!\brief Authenticate using the OpenLDAP library + Asterisk tries to bind to the configured LDAP server using user provided + credentials. + + \param user the user we want to authenticate + \param password the user provided password + + \return -1 on error, 0 on authentication success + */ +int ast_ldap_authenticate(const char* user, const char* password) +{ + int res = -1; + int result = -1; + int bind_result = -1; + LDAP *connection = NULL; + char ldap_filter[256]; + char *dn = NULL; + int num_entry = 0; + LDAPMessage *ldap_entry = NULL; + LDAPMessage *ldap_result = NULL; + + + /* Let's search for the DN we want to authenticate with */ + snprintf(ldap_filter, sizeof(ldap_filter), "(%s=%s)", username_attr, user); + result = ldap_search_s(ldapConn, dbbasedn, + LDAP_SCOPE_SUBTREE, ldap_filter, NULL, 0, + &ldap_result); + + /* some servers may close the connection unexpectedly, so let's + try to reconnect and start a new search operation */ + if (result != LDAP_SUCCESS) { + switch (result) { + case LDAP_SERVER_DOWN: + case LDAP_BUSY: + case LDAP_UNAVAILABLE: + if (ldap_reconnect() < 0) { + ast_log(LOG_ERROR, "Lost connection to LDAP server\n"); + return -1; + } else + result = ldap_search_s(ldapConn, dbbasedn, + LDAP_SCOPE_SUBTREE, ldap_filter, NULL, 0, + &ldap_result); + break; + default: + ast_log(LOG_ERROR, "LDAP search failed : %s\n", ldap_err2string(result)); + return -1; + } + } + + num_entry = ldap_count_entries(ldapConn, ldap_result); + + if (!num_entry) { + ast_log(LOG_ERROR, "No entry found. Filter : %s\n", ldap_filter); + return -1; + } + /* Retrieve DN, only process the first entry */ + ldap_entry = ldap_first_entry(ldapConn, ldap_result); + dn = ldap_get_dn(ldapConn, ldap_entry); + if (!dn) { + ast_log(LOG_ERROR, "DN not found. Filter : %s\n", ldap_filter); + return -1; + } + + /* We need to create a new LDAP connection to the server. Otherwise, + the server would close the global connection in case of + authentication failure.*/ + + if (!(connection = ldap_init(dbhost, dbport))) { + ast_log(LOG_ERROR, "Failed to connect to %s on port %d.\n", dbhost, dbport); + return -1; + } + bind_result = ldap_simple_bind_s(connection, dn, password); + if (bind_result == LDAP_SUCCESS) + res = 0; + else + ast_log(LOG_ERROR, "Bind failed: %s\n", ldap_err2string(bind_result)); + + ldap_unbind(connection); + ldap_msgfree(ldap_result); + ldap_memfree(dn); + + return res; +} + + #endif /* HAVE_OPENLDAP */ #ifdef HAVE_RADIUS @@ -347,27 +461,24 @@ } /*!\brief Authenticate using the radiusclient-ng library - Acting as a RADIUS client, Asterisk sends a RADIUS authentication request - to a RADIUS server, and waits for the answer. + Acting as a RADIUS client, Asterisk sends a digest RADIUS authentication + request to a RADIUS server, and waits for the answer. The RADIUS client configuration file location is set in RC_CONFIG_FILE \param user the user we want to authenticate - \param params a structure that contains the RADIUS attributes needed + \param params a structure that contains the RADIUS Digest attributes needed to build the authentication request \return -1 on error, 0 on authentication success */ -int ast_digest_authenticate(const char* user, struct digest_parameters *params) +int ast_radius_digest_authenticate(const char* user, struct digest_parameters *params) { - int res; + int res = -1; int i; struct digest_parameters aux; VALUE_PAIR *send, *received; char msg[4096]; - - ast_mutex_lock(&rc_lock); - send = NULL; aux.num_attrs = params->num_attrs; @@ -403,16 +514,58 @@ if (received) rc_avpair_free(received); - ast_mutex_unlock(&rc_lock); + return res; +} - /* send result */ +/*!\brief Authenticate using the radiusclient-ng library + Acting as a RADIUS client, Asterisk sends a simple RADIUS authentication + request to a RADIUS server, and waits for the answer. + + The RADIUS client configuration file location is set in RC_CONFIG_FILE + + \param user the user we want to authenticate + \param password the user provided password + + \return -1 on error, 0 on authentication success + */ +int ast_radius_authenticate(const char* user, const char* pass) { + int res = -1; + VALUE_PAIR *send, *received; + char username[128]; + char password[128]; + UINT4 service; + char msg[4096]; + send = NULL; + + ast_copy_string(username, user, sizeof(username)); + ast_copy_string(password, pass, sizeof(password)); + + /* Add a User-Name attribute */ + if (rc_avpair_add(rh, &send, PW_USER_NAME, username, -1, 0) == NULL) + return -1; + /* Add a User-Password attribute */ + if (rc_avpair_add(rh, &send, PW_USER_PASSWORD, password, -1, 0) == NULL) + return -1; + /* Add a Service-Type attribute */ + service = PW_AUTHENTICATE_ONLY; + if (rc_avpair_add(rh, &send, PW_SERVICE_TYPE, &service, -1, 0) == NULL) + return -1; + + res = rc_auth(rh, 0, send, &received, msg); + + /* free resources */ + rc_avpair_free(send); + if (received) + rc_avpair_free(received); + return res; } + #endif /* HAVE_RADIUS */ static int parse_config(void) { - const struct ast_config *config; + struct ast_config *config; const char *s; config = ast_config_load(RES_AUTH_CONF); @@ -459,26 +612,30 @@ ast_log(LOG_ERROR, "user_password_attribute is needed.\n"); } else ast_copy_string(userpw_attr, s, sizeof(userpw_attr)); +#endif /* HAVE_OPENLDAP */ + ast_config_destroy(config); } - ast_log(LOG_NOTICE, "LDAP Host: %s\n", dbhost); - ast_log(LOG_NOTICE, "LDAP Port: %i\n", dbport); - ast_log(LOG_NOTICE, "LDAP User: %s\n", dbuser); - ast_log(LOG_NOTICE, "LDAP Password: %s\n", dbpass); - ast_log(LOG_NOTICE, "LDAP BaseDN: %s\n", dbbasedn); - ast_log(LOG_NOTICE, "LDAP User name attribute: %s\n", username_attr); - ast_log(LOG_NOTICE, "LDAP User password attribute: %s\n", userpw_attr); +#ifdef HAVE_OPENLDAP + ast_log(LOG_DEBUG, "LDAP Host: %s\n", dbhost); + ast_log(LOG_DEBUG, "LDAP Port: %i\n", dbport); + ast_log(LOG_DEBUG, "LDAP User: %s\n", dbuser); + ast_log(LOG_DEBUG, "LDAP Password: %s\n", dbpass); + ast_log(LOG_DEBUG, "LDAP BaseDN: %s\n", dbbasedn); + ast_log(LOG_DEBUG, "LDAP User name attribute: %s\n", username_attr); + ast_log(LOG_DEBUG, "LDAP User password attribute: %s\n", userpw_attr); #endif /* HAVE_OPENLDAP */ - ast_config_destroy(config); return 1; } static int unload_module(void) { +#ifdef HAVE_OPENLDAP ldap_disconnect(); +#endif /* HAVE_OPENLDAP */ ast_log(LOG_NOTICE, "res_auth unloaded.\n"); return 0; } Index: res/Makefile =================================================================== --- res/Makefile (revision 62036) +++ res/Makefile (working copy) @@ -21,6 +21,8 @@ LOADABLE_MODS:= endif +EMBEDDED_MODS:=$(EMBEDDED_MODS) res_auth + all: _all include $(ASTTOPDIR)/Makefile.moddir_rules