📄 auth_module.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi <pekka.pessi@nokia.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * *//**@CFILE auth_module.c * @brief Authentication verification module * * The authentication module provides server or proxy-side authentication * verification for network elements like registrars, presence servers, and * proxies. * * @author Pekka Pessi <Pekka.Pessi@nokia.com>. * * @date Created: Wed Apr 11 15:14:03 2001 ppessi */#include "config.h"#include <stddef.h>#include <stdlib.h>#include <string.h>#include <stdio.h>#include <assert.h>#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>#include <sofia-sip/auth_digest.h>#include "iptsec_debug.h"#if HAVE_FUNC#elif HAVE_FUNCTION#define __func__ __FUNCTION__#elsestatic char const __func__[] = "auth_mod";#endif#include <sofia-sip/su_debug.h>#include <sofia-sip/su_wait.h>#include <sofia-sip/su_alloc.h>#include <sofia-sip/su_tagarg.h>#include <sofia-sip/base64.h>#include <sofia-sip/su_md5.h>#include <sofia-sip/msg_parser.h>#include <sofia-sip/msg_date.h>#include "sofia-sip/auth_module.h"#include "sofia-sip/auth_plugin.h"#define APW_HASH(apw) ((apw)->apw_index)char const auth_internal_server_error[] = "Internal server error";static void auth_call_scheme_destructor(void *);static void auth_md5_hmac_key(auth_mod_t *am);HTABLE_PROTOS_WITH(auth_htable, aht, auth_passwd_t, usize_t, unsigned);HTABLE_BODIES_WITH(auth_htable, aht, auth_passwd_t, APW_HASH, usize_t, unsigned);/**Allocate an authentication module instance. * * The function auth_mod_alloc() allocates an authentication module object. * */auth_mod_t *auth_mod_alloc(auth_scheme_t *scheme, tag_type_t tag, tag_value_t value, ...){ auth_mod_t *am = NULL; if ((am = su_home_new(scheme->asch_size))) { am->am_scheme = scheme; su_home_destructor(am->am_home, auth_call_scheme_destructor); } return am;}/**Initialize an authentication module instance. * * The function auth_mod_init_default() initializes an authentication module * object used to authenticate the requests. * * @param am * @param base * @param root * @param tag,value,... tagged argument list * * @TAGS * AUTHTAG_REALM(), AUTHTAG_OPAQUE(), AUTHTAG_DB(), AUTHTAG_QOP(), * AUTHTAG_ALGORITHM(), AUTHTAG_EXPIRES(), AUTHTAG_NEXT_EXPIRES(), * AUTHTAG_BLACKLIST(), AUTHTAG_FORBIDDEN(), AUTHTAG_ANONYMOUS(), * AUTHTAG_FAKE(), AUTHTAG_ALLOW(), AUTHTAG_REMOTE(), and * AUTHTAG_MASTER_KEY(). * * @return 0 if successful * @return -1 upon an error */int auth_init_default(auth_mod_t *am, auth_scheme_t *base, su_root_t *root, tag_type_t tag, tag_value_t value, ...){ int retval = 0; ta_list ta; char const *realm = NULL, *opaque = NULL, *db = NULL, *allows = NULL; char const *qop = NULL, *algorithm = NULL; unsigned expires = 60 * 60, next_expires = 5 * 60; unsigned max_ncount = 0; unsigned blacklist = 5; int forbidden = 0; int anonymous = 0; int fake = 0; url_string_t const *remote = NULL; char const *master_key = "fish"; char *s; ta_start(ta, tag, value); /* Authentication stuff */ tl_gets(ta_args(ta), AUTHTAG_REALM_REF(realm), AUTHTAG_OPAQUE_REF(opaque), AUTHTAG_DB_REF(db), AUTHTAG_QOP_REF(qop), AUTHTAG_ALGORITHM_REF(algorithm), AUTHTAG_EXPIRES_REF(expires), AUTHTAG_NEXT_EXPIRES_REF(next_expires), AUTHTAG_MAX_NCOUNT_REF(max_ncount), AUTHTAG_BLACKLIST_REF(blacklist), AUTHTAG_FORBIDDEN_REF(forbidden), AUTHTAG_ANONYMOUS_REF(anonymous), AUTHTAG_FAKE_REF(fake), AUTHTAG_ALLOW_REF(allows), AUTHTAG_REMOTE_REF(remote), AUTHTAG_MASTER_KEY_REF(master_key), TAG_NULL()); if (!realm) realm = "*"; if (!allows) allows = "ACK, BYE, CANCEL"; am->am_realm = su_strdup(am->am_home, realm); am->am_opaque = su_strdup(am->am_home, opaque); am->am_db = su_strdup(am->am_home, db); s = su_strdup(am->am_home, allows); if (s) msg_commalist_d(am->am_home, &s, &am->am_allow, NULL); am->am_expires = expires; am->am_next_exp = next_expires; am->am_max_ncount = max_ncount; am->am_blacklist = blacklist; am->am_forbidden = forbidden; am->am_anonymous = anonymous; am->am_fake = fake; am->am_remote = url_hdup(am->am_home, (url_t *)remote); am->am_algorithm = algorithm ? su_strdup(am->am_home, algorithm) : "MD5"; am->am_nextnonce = !algorithm || strcasecmp(algorithm, "MD5") == 0; if (next_expires == 0) am->am_nextnonce = 0; am->am_qop = su_strdup(am->am_home, qop); if (master_key) { su_md5_t md5[1]; su_md5_init(md5); su_md5_strupdate(md5, master_key); su_md5_strupdate(md5, "70P 53KR37"); su_md5_digest(md5, am->am_master_key); } auth_md5_hmac_key(am); /* Make sure that we have something that can be used to identify credentials */ if (am->am_opaque && strcmp(am->am_opaque, "*") == 0) {#ifndef HOST_NAME_MAX#define HOST_NAME_MAX 255#endif char hostname[HOST_NAME_MAX + 1]; su_md5_t md5[1]; uint8_t hmac[6]; gethostname(hostname, sizeof hostname); auth_md5_hmac_init(am, md5); su_md5_strupdate(md5, hostname); su_md5_update(md5, ":", 1); if (am->am_remote) url_update(md5, am->am_remote); auth_md5_hmac_digest(am, md5, hmac, sizeof hmac); base64_e(hostname, sizeof hostname, hmac, sizeof hmac); am->am_opaque = su_strdup(am->am_home, hostname); if (!am->am_opaque) { retval = -1; SU_DEBUG_1(("%s: cannot create unique identifier\n", __func__)); } } if (retval < 0) ; else if (db) { retval = auth_readdb(am); if (retval == -1) { int err = errno; SU_DEBUG_1(("auth-module: %s: %s\n", am->am_db, strerror(err))); errno = err; } } else { retval = auth_htable_resize(am->am_home, am->am_users, 0); } ta_end(ta); return retval;}/** Destroy (a reference to) an authentication module. */void auth_mod_destroy(auth_mod_t *am){ su_home_unref(am->am_home);}/** Call scheme-specific destructor function. */static void auth_call_scheme_destructor(void *arg){ auth_mod_t *am = arg; am->am_scheme->asch_destroy(am);}/** Do-nothing destroy function. * * The auth_destroy_default() is the default member function called by * auth_mod_destroy(). */void auth_destroy_default(auth_mod_t *am){}/** Create a new reference to authentication module. */auth_mod_t *auth_mod_ref(auth_mod_t *am){ return (auth_mod_t *)su_home_ref(am->am_home);}/** Destroy a reference to an authentication module. */void auth_mod_unref(auth_mod_t *am){ su_home_unref(am->am_home);}/** Get authenticatin module name. @NEW_1_12_4. */char const *auth_mod_name(auth_mod_t *am){ return am ? am->am_scheme->asch_method : "<nil>";}/** Initialize a auth_status_t stucture. * * @retval NULL upon an error * @relates auth_status_t */auth_status_t *auth_status_init(void *p, isize_t size){ return auth_status_init_with(p, size, 500, auth_internal_server_error);}/** Initialize a auth_status_t stucture. * * @retval NULL upon an error * @relates auth_status_t */auth_status_t *auth_status_init_with(void *p, isize_t size, int status, char const *phrase){ auth_status_t *as; if (!p || size < (sizeof *as)) return NULL; if (size > INT_MAX) size = INT_MAX; as = memset(p, 0, size); as->as_home->suh_size = (int)size; /* su_home_init(as->as_home); */ as->as_status = status, as->as_phrase = phrase; return as;}/** Allocate a new auth_status_t structure. @relates auth_status_t */auth_status_t *auth_status_new(su_home_t *home){ auth_status_t *as = su_home_clone(home, (sizeof *as)); if (as) { as->as_status = 500; as->as_phrase = auth_internal_server_error; } return as;}/** Create a new reference to an auth_status_t structure. * @relates auth_status_t */auth_status_t *auth_status_ref(auth_status_t *as){ return (auth_status_t *)su_home_ref(as->as_home);}/** Destroy (a reference to) an auth_status_t structure. @relates auth_status_t */void auth_status_unref(auth_status_t *as){ su_home_unref(as->as_home);}/** Authenticate user. * * The function auth_mod_method() invokes scheme-specific authentication * operation where the user's credentials are checked using scheme-specific * method. The authentication result along with an optional challenge header * is stored in the @a as structure. * * @param am pointer to authentication module object [in] * @param as pointer to authentication status structure [in/out] * @param credentials pointer to a header with user's credentials [in] * @param ach pointer to a structure describing challenge [in] * * The @a ach structure defines what kind of response and challenge header * is returned to the user. For example, a server authentication is * implemented with 401 response code and phrase along with WWW-Authenticate * header template in the @a ach structure. * * The auth_mod_method() returns the authentication result in the * #auth_mod_t @a as structure. The @a as->as_status describes the result * as follows: * - <i>as->as_status == 0</i> authentication is successful * - <i>as->as_status == 100</i> authentication is pending * - <i>as->as_status >= 400</i> authentication fails, * return as_status as an error code to client * * When the authentication is left pending, the client must set the * as_callback pointer in @a as structure to an appropriate callback * function. The callback is invoked when the authentication is completed, * either successfully or with an error. * * Note that the authentication module may generate a new challenge each * time authentication is used (e.g., Digest using MD5 algorithm). Such a * challenge header is stored in the @a as->as_response return-value field. * * @note The authentication plugin may use the given reference to @a as, @a * credentials and @a ach structures until the asynchronous authentication * completes. Therefore, they should not be allocated from stack unless * application uses strictly synchronous authentication schemes only (Basic * and Digest). * * @note This function should be called auth_mod_check(). */void auth_mod_verify(auth_mod_t *am, auth_status_t *as, msg_auth_t *credentials, auth_challenger_t const *ach){ char const *wildcard, *host; if (!am || !as || !ach) return; wildcard = strchr(am->am_realm, '*'); host = as->as_domain; /* Initialize per-request realm */ if (as->as_realm) ; else if (!wildcard) { as->as_realm = am->am_realm; } else if (!host) { return; /* Internal error */ } else if (strcmp(am->am_realm, "*") == 0) { as->as_realm = host; } else { /* Replace * with hostpart */ as->as_realm = su_sprintf(as->as_home, "%.*s%s%s", (int)(wildcard - am->am_realm), am->am_realm, host, wildcard + 1); } am->am_scheme->asch_check(am, as, credentials, ach);}/** Make a challenge header. * * This function invokes plugin-specific member function generating a * challenge header. Client uses the challenge header contents when * prompting the user for a username and password then generates its * credential header using the parameters given in the challenge header. * * @param am pointer to authentication module object * @param as pointer to authentication status structure (return-value) * @param ach pointer to a structure describing challenge * * The auth_mod_challenge() returns the challenge header, appropriate * response code and reason phrase in the #auth_status_t structure. The * auth_mod_challenge() is currently always synchronous function. */void auth_mod_challenge(auth_mod_t *am, auth_status_t *as, auth_challenger_t const *ach){ if (am && as && ach) am->am_scheme->asch_challenge(am, as, ach);}/** Cancel asynchronous authentication. * * The auth_mod_cancel() function cancels a pending authentication. * Application can reclaim the authentication status, credential and * challenger objects by using auth_mod_cancel(). */void auth_mod_cancel(auth_mod_t *am, auth_status_t *as){ if (am && as) am->am_scheme->asch_cancel(am, as);}/** Do-nothing cancel function. * * The auth_cancel_default() is the default member function called by * auth_mod_cancel(). */void auth_cancel_default(auth_mod_t *am, auth_status_t *as){}/* ====================================================================== *//* Basic authentication scheme */static void auth_method_basic_x(auth_mod_t *am, auth_status_t *as, msg_auth_t *au, auth_challenger_t const *ach);auth_scheme_t auth_scheme_basic[1] = {{ "Basic", /* asch_method */ sizeof (auth_mod_t), /* asch_size */ auth_init_default, /* asch_init */ auth_method_basic_x, /* asch_check */ auth_challenge_basic, /* asch_challenge */ auth_cancel_default, /* asch_cancel */ auth_destroy_default /* asch_destroy */ }};/**Authenticate a request with @b Basic authentication. * * This function reads user database before authentication, if needed. */staticvoid auth_method_basic_x(auth_mod_t *am, auth_status_t *as, msg_auth_t *au, auth_challenger_t const *ach){ if (am) { auth_readdb_if_needed(am); auth_method_basic(am, as, au, ach); }}/** Authenticate a request with @b Basic authentication scheme. * */void auth_method_basic(auth_mod_t *am,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -