📄 ne_auth.c
字号:
/* HTTP Authentication routines Copyright (C) 1999-2004, Joe Orton <joe@manyfish.co.uk> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA*//* HTTP Authentication, as per RFC2617. * * TODO: * - Test auth-int support */#include "config.h"#include <sys/types.h>#ifdef HAVE_SYS_TIME_H#include <sys/time.h>#endif#ifdef HAVE_STDLIB_H#include <stdlib.h>#endif#ifdef HAVE_STRING_H#include <string.h>#endif#ifdef HAVE_STRINGS_H#include <strings.h>#endif#ifdef HAVE_UNISTD_H#include <unistd.h> /* for getpid() */#endif#ifdef WIN32#include <windows.h> /* for GetCurrentThreadId() etc */#endif#ifdef NEON_SSL#include <openssl/rand.h>#endif#include <time.h>#include "ne_md5.h"#include "ne_dates.h"#include "ne_request.h"#include "ne_auth.h"#include "ne_string.h"#include "ne_utils.h"#include "ne_alloc.h"#include "ne_uri.h"#include "ne_i18n.h"#ifdef HAVE_GSSAPI#ifdef HAVE_GSSAPI_GSSAPI_H#include <gssapi/gssapi.h>#ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H#include <gssapi/gssapi_generic.h>#endif#else#include <gssapi.h>#endif#endif/* TODO: should remove this eventually. Need it for * ne_pull_request_body. */#include "ne_private.h"#define HOOK_SERVER_ID "http://webdav.org/neon/hooks/server-auth"#define HOOK_PROXY_ID "http://webdav.org/neon/hooks/proxy-auth"/* The authentication scheme we are using */typedef enum { auth_scheme_basic, auth_scheme_digest, auth_scheme_gssapi} auth_scheme;typedef enum { auth_alg_md5, auth_alg_md5_sess, auth_alg_unknown} auth_algorithm;/* Selected method of qop which the client is using */typedef enum { auth_qop_none, auth_qop_auth, auth_qop_auth_int} auth_qop;/* A challenge */struct auth_challenge { auth_scheme scheme; const char *realm; const char *nonce; const char *opaque; unsigned int stale:1; /* if stale=true */ unsigned int got_qop:1; /* we were given a qop directive */ unsigned int qop_auth:1; /* "auth" token in qop attrib */ unsigned int qop_auth_int:1; /* "auth-int" token in qop attrib */ auth_algorithm alg; struct auth_challenge *next;};static const struct auth_class { const char *id, *req_hdr, *resp_hdr, *resp_info_hdr, *fail_msg; int status_code, fail_code;} ah_server_class = { HOOK_SERVER_ID, "Authorization", "WWW-Authenticate", "Authentication-Info", N_("Server was not authenticated correctly."), 401, NE_AUTH}, ah_proxy_class = { HOOK_PROXY_ID, "Proxy-Authorization", "Proxy-Authenticate", "Proxy-Authentication-Info", N_("Proxy server was not authenticated correctly."), 407, NE_PROXYAUTH };/* Authentication session state. */typedef struct { ne_session *sess; /* Which context will auth challenges be accepted? */ enum { AUTH_ANY, /* ignore nothing. */ AUTH_CONNECT, /* only in response to a CONNECT request. */ AUTH_NOTCONNECT /* only in non-CONNECT responsees */ } context; /* Specifics for server/proxy auth. FIXME: need a better field * name! */ const struct auth_class *spec; /* The scheme used for this authentication session */ auth_scheme scheme; /* The callback used to request new username+password */ ne_auth_creds creds; void *userdata; /*** Session details ***/ /* The username and password we are using to authenticate with */ char username[NE_ABUFSIZ]; /* Whether we CAN supply authentication at the moment */ unsigned int can_handle:1; /* This used for Basic auth */ char *basic; #ifdef HAVE_GSSAPI /* This used for GSSAPI auth */ char *gssapi_token;#endif /* These all used for Digest auth */ char *realm; char *nonce; char *cnonce; char *opaque; auth_qop qop; auth_algorithm alg; unsigned int nonce_count; /* The ASCII representation of the session's H(A1) value */ char h_a1[33]; /* Temporary store for half of the Request-Digest * (an optimisation - used in the response-digest calculation) */ struct ne_md5_ctx stored_rdig; /* Details of server... needed to reconstruct absoluteURI's when * necessary */ const char *host; const char *uri_scheme; unsigned int port; int attempt;} auth_session;struct auth_request { /*** Per-request details. ***/ ne_request *request; /* the request object. */ /* The method and URI we are using for the current request */ const char *uri; const char *method; /* Whether we WILL supply authentication for this request or not */ unsigned int will_handle:1; /* Used for calculation of H(entity-body) of the response */ struct ne_md5_ctx response_body; /* Results of response-header callbacks */ char *auth_hdr, *auth_info_hdr;};static void clean_session(auth_session *sess) { sess->can_handle = 0; NE_FREE(sess->basic); NE_FREE(sess->nonce); NE_FREE(sess->cnonce); NE_FREE(sess->opaque); NE_FREE(sess->realm);#ifdef HAVE_GSSAPI NE_FREE(sess->gssapi_token);#endif}/* Returns client nonce string. */static char *get_cnonce(void) { char ret[33]; unsigned char data[256], tmp[16]; struct ne_md5_ctx hash; ne_md5_init_ctx(&hash);#ifdef NEON_SSL if (RAND_status() == 1 && RAND_pseudo_bytes(data, sizeof data) >= 0) ne_md5_process_bytes(data, sizeof data, &hash); else {#endif /* Fallback sources of random data: all bad, but no good sources * are available. */ /* Uninitialized stack data; yes, happy valgrinders, this is * supposed to be here. */ ne_md5_process_bytes(data, sizeof data, &hash); #ifdef HAVE_GETTIMEOFDAY { struct timeval tv; if (gettimeofday(&tv, NULL) == 0) ne_md5_process_bytes(&tv, sizeof tv, &hash); }#else /* HAVE_GETTIMEOFDAY */ { time_t t = time(NULL); ne_md5_process_bytes(&t, sizeof t, &hash); }#endif {#ifdef WIN32 DWORD pid = GetCurrentThreadId();#else pid_t pid = getpid();#endif ne_md5_process_bytes(&pid, sizeof pid, &hash); }#ifdef NEON_SSL }#endif ne_md5_finish_ctx(&hash, tmp); ne_md5_to_ascii(tmp, ret); return ne_strdup(ret);}static int get_credentials(auth_session *sess, char *pwbuf) { return sess->creds(sess->userdata, sess->realm, sess->attempt++, sess->username, pwbuf);}/* Examine a Basic auth challenge. * Returns 0 if an valid challenge, else non-zero. */static int basic_challenge(auth_session *sess, struct auth_challenge *parms) { char *tmp, password[NE_ABUFSIZ]; /* Verify challenge... must have a realm */ if (parms->realm == NULL) { return -1; } NE_DEBUG(NE_DBG_HTTPAUTH, "Got Basic challenge with realm [%s]\n", parms->realm); clean_session(sess); sess->realm = ne_strdup(parms->realm); if (get_credentials(sess, password)) { /* Failed to get credentials */ return -1; } sess->scheme = auth_scheme_basic; tmp = ne_concat(sess->username, ":", password, NULL); sess->basic = ne_base64((unsigned char *)tmp, strlen(tmp)); ne_free(tmp); /* Paranoia. */ memset(password, 0, sizeof password); return 0;}/* Add Basic authentication credentials to a request */static char *request_basic(auth_session *sess) { return ne_concat("Basic ", sess->basic, "\r\n", NULL);}#ifdef HAVE_GSSAPI/* Add GSSAPI authentication credentials to a request */static char *request_gssapi(auth_session *sess) { return ne_concat("Negotiate ", sess->gssapi_token, "\r\n", NULL);}static int get_gss_name(gss_name_t *server, auth_session *sess){ unsigned int major_status, minor_status; gss_buffer_desc token = GSS_C_EMPTY_BUFFER; token.value = ne_concat("HTTP@", sess->sess->server.hostname, NULL); token.length = strlen(token.value); major_status = gss_import_name(&minor_status, &token, GSS_C_NT_HOSTBASED_SERVICE, server); return GSS_ERROR(major_status) ? -1 : 0;}/* Examine a GSSAPI auth challenge; returns 0 if a valid challenge, * else non-zero. */static int gssapi_challenge(auth_session *sess, struct auth_challenge *parms) { gss_ctx_id_t context; gss_name_t server_name; unsigned int major_status, minor_status; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; clean_session(sess); if (get_gss_name(&server_name, sess)) return -1; major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL, &context, server_name, GSS_C_NO_OID, 0, GSS_C_INDEFINITE, GSS_C_NO_CHANNEL_BINDINGS, &input_token, NULL, &output_token, NULL, NULL); gss_release_name(&minor_status, &server_name); if (GSS_ERROR(major_status)) { NE_DEBUG(NE_DBG_HTTPAUTH, "gss_init_sec_context failed.\n"); return -1; } if (output_token.length == 0) return -1; sess->gssapi_token = ne_base64(output_token.value, output_token.length); gss_release_buffer(&major_status, &output_token); NE_DEBUG(NE_DBG_HTTPAUTH, "Base64 encoded GSSAPI challenge: %s.\n", sess->gssapi_token); sess->scheme = auth_scheme_gssapi; return 0;}#endif/* Examine a digest challenge: return 0 if it is a valid Digest challenge, * else non-zero. */static int digest_challenge(auth_session *sess, struct auth_challenge *parms)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -