From 4fab732ab16c3cdd448f366bc4288bb134e43410 Mon Sep 17 00:00:00 2001 From: Alex Graveley Date: Thu, 23 Aug 2001 07:01:57 +0000 Subject: [PATCH] Microsoft NTLM authentication support compliments of Dan Winship 2001-08-23 Alex Graveley * src/soup-core/soup-ntlm.[ch]: Microsoft NTLM authentication support compliments of Dan Winship (danw@ximian.com). * src/soup-core/Makefile.am (libsoup_la_SOURCES): Add soup-ntlm.[ch] * src/soup-core/soup-auth.c (soup_auth_new_from_header): Set auth context before header parsing. Add NTLM case. Call auth->parse_func instead of hardcoding. (soup_auth_invalidates_prior): add NTLM, which should always invalidate. (ntlm_new): impl. (ntlm_free): impl. (ntlm_parse): impl. Hack to get domain from the uri's authmech field. (ntlm_auth): impl. * src/soup-core/soup-dav-server.c (get_depth): return 0 for unknown value. * src/soup-core/soup-server.h: Remove SoupServerAnonymousToken and add SoupServerNTLMToken. --- ChangeLog | 24 ++ libsoup/Makefile.am | 2 + libsoup/soup-auth.c | 142 ++++++- libsoup/soup-ntlm.c | 1020 +++++++++++++++++++++++++++++++++++++++++++++++++ libsoup/soup-ntlm.h | 32 ++ libsoup/soup-server.h | 30 +- 6 files changed, 1225 insertions(+), 25 deletions(-) create mode 100644 libsoup/soup-ntlm.c create mode 100644 libsoup/soup-ntlm.h diff --git a/ChangeLog b/ChangeLog index 467b115..a0e345b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,27 @@ +2001-08-23 Alex Graveley + + * src/soup-core/soup-ntlm.[ch]: Microsoft NTLM authentication + support compliments of Dan Winship (danw@ximian.com). + + * src/soup-core/Makefile.am (libsoup_la_SOURCES): Add soup-ntlm.[ch] + + * src/soup-core/soup-auth.c (soup_auth_new_from_header): Set auth + context before header parsing. Add NTLM case. Call + auth->parse_func instead of hardcoding. + (soup_auth_invalidates_prior): add NTLM, which should always + invalidate. + (ntlm_new): impl. + (ntlm_free): impl. + (ntlm_parse): impl. Hack to get domain from the uri's authmech + field. + (ntlm_auth): impl. + + * src/soup-core/soup-dav-server.c (get_depth): return 0 for + unknown value. + + * src/soup-core/soup-server.h: Remove SoupServerAnonymousToken and + add SoupServerNTLMToken. + 2001-08-20 Joe Shaw * configure.in: Bump up version to 0.4.4 diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am index b0a29de..5bde3d4 100644 --- a/libsoup/Makefile.am +++ b/libsoup/Makefile.am @@ -52,6 +52,8 @@ libsoup_la_SOURCES = \ soup-headers.c \ soup-message.c \ soup-misc.c \ + soup-ntlm.h \ + soup-ntlm.c \ soup-parser.c \ soup-private.h \ soup-queue.c \ diff --git a/libsoup/soup-auth.c b/libsoup/soup-auth.c index 0fa6910..822f7c6 100644 --- a/libsoup/soup-auth.c +++ b/libsoup/soup-auth.c @@ -34,12 +34,16 @@ #include "soup-context.h" #include "soup-message.h" #include "soup-private.h" +#include "soup-ntlm.h" typedef enum { SOUP_AUTH_BASIC, - SOUP_AUTH_DIGEST + SOUP_AUTH_DIGEST, + SOUP_AUTH_NTLM, } SoupAuthType; +typedef void (*SoupParseFunc) (SoupAuth *auth, const gchar *header); + typedef char *(*SoupAuthFunc) (SoupAuth *auth, SoupMessage *message); typedef void (*SoupAuthFreeFn) (SoupAuth *auth); @@ -47,6 +51,7 @@ typedef void (*SoupAuthFreeFn) (SoupAuth *auth); struct _SoupAuth { SoupContext *context; SoupAuthType type; + SoupParseFunc parse_func; SoupAuthFunc auth_func; SoupAuthFreeFn free_func; }; @@ -89,12 +94,15 @@ basic_auth_func (SoupAuth *auth, SoupMessage *message) } static void -soup_auth_basic_parse_header (SoupAuth *auth, const char *header) +basic_parse_func (SoupAuth *auth, const char *header) { SoupAuthBasic *basic = (SoupAuthBasic *) auth; - GHashTable *tokens = parse_param_list (header); + GHashTable *tokens; + + header += sizeof ("Basic"); - g_return_if_fail (tokens); + tokens = parse_param_list (header); + if (!tokens) return; basic->realm = copy_token_if_exists (tokens, "realm"); @@ -119,6 +127,7 @@ soup_auth_new_basic (void) basic = g_new0 (SoupAuthBasic, 1); auth = (SoupAuth *) basic; auth->type = SOUP_AUTH_BASIC; + auth->parse_func = basic_parse_func; auth->auth_func = basic_auth_func; auth->free_func = basic_free; @@ -362,13 +371,16 @@ decode_algorithm (const char *name) } static void -soup_auth_digest_parse_header (SoupAuth *auth, const char *header) +digest_parse_func (SoupAuth *auth, const char *header) { SoupAuthDigest *digest = (SoupAuthDigest *) auth; - GHashTable *tokens = parse_param_list (header); + GHashTable *tokens; char *tmp, *ptr; - g_return_if_fail (tokens); + header += sizeof ("Digest"); + + tokens = parse_param_list (header); + if (!tokens) return; digest->realm = copy_token_if_exists (tokens, "realm"); digest->nonce = copy_token_if_exists (tokens, "nonce"); @@ -427,6 +439,7 @@ soup_auth_new_digest (void) digest = g_new0 (SoupAuthDigest, 1); auth = (SoupAuth *) digest; auth->type = SOUP_AUTH_DIGEST; + auth->parse_func = digest_parse_func; auth->auth_func = digest_auth_func; auth->free_func = digest_free; @@ -444,6 +457,106 @@ soup_auth_new_digest (void) /* + * NTLM Authentication Support + */ + +typedef struct { + SoupAuth auth; + gchar *request; + gchar *response; +} SoupAuthNTLM; + +static gchar * +ntlm_auth (SoupAuth *sa, SoupMessage *msg) +{ + SoupAuthNTLM *auth = (SoupAuthNTLM *) sa; + + return auth->response ? auth->response : auth->request; +} + + +/* + * FIXME: Because NTLM is a two step process, we parse the host and domain out + * of the context's uri twice. This is because there is no way to reparse + * a new header with an existing SoupAuth, so a new one is created for + * each negotiation step. + */ +static void +ntlm_parse (SoupAuth *sa, const char *header) +{ + SoupAuthNTLM *auth = (SoupAuthNTLM *) sa; + const SoupUri *uri = soup_context_get_uri (auth->auth.context); + gchar *idx, *host, *domain = NULL; + + idx = strchr (uri->host, '.'); + if (idx) + host = g_strndup (uri->host, idx - uri->host); + else + host = g_strdup (uri->host); + + if (uri->authmech) { + idx = strstr (uri->authmech, "domain="); + if (idx) { + gint len; + + idx += sizeof ("domain=") - 1; + + len = strcspn (idx, ",; "); + if (len) + domain = g_strndup (idx, len); + else + domain = g_strdup (idx); + } + } + + if (strlen (header) > sizeof ("NTLM")) + auth->request = soup_ntlm_request (host, + domain ? domain : "UNKNOWN"); + else { + gchar lm_hash [21], nt_hash [21]; + + soup_ntlm_lanmanager_hash (uri->passwd, lm_hash); + soup_ntlm_nt_hash (uri->passwd, nt_hash); + + auth->response = + soup_ntlm_response (header, + uri->user, + (gchar *) &lm_hash, + (gchar *) &nt_hash, + host, + domain ? domain : "UNKNOWN"); + } + + g_free (host); + g_free (domain); +} + +static void +ntlm_free (SoupAuth *sa) +{ + SoupAuthNTLM *auth = (SoupAuthNTLM *) sa; + + g_free (auth->request); + g_free (auth->response); + g_free (auth); +} + +static SoupAuth * +ntlm_new (void) +{ + SoupAuth *auth; + + auth = g_new0 (SoupAuth, 1); + auth->type = SOUP_AUTH_NTLM; + auth->parse_func = ntlm_parse; + auth->auth_func = ntlm_auth; + auth->free_func = ntlm_free; + + return auth; +} + + +/* * Generic Authentication Interface */ @@ -452,21 +565,20 @@ soup_auth_new_from_header (SoupContext *context, const char *header) { SoupAuth *auth; - if (g_strncasecmp (header, "Basic", 5) == 0) { + if (!g_strncasecmp (header, "Basic", sizeof ("Basic") - 1)) auth = soup_auth_new_basic (); - soup_auth_basic_parse_header (auth, header + 6); - } - else if (g_strncasecmp (header, "Digest", 6) == 0) { + else if (!g_strncasecmp (header, "Digest", sizeof ("Digest") - 1)) auth = soup_auth_new_digest (); - soup_auth_digest_parse_header (auth, header + 7); - } + else if (!g_strncasecmp (header, "NTLM", sizeof ("NTLM") - 1)) + auth = ntlm_new (); else { - g_warning ("Authentication type not supported"); return NULL; } auth->context = context; + auth->parse_func (auth, header); + return auth; } @@ -495,6 +607,8 @@ soup_auth_invalidates_prior (SoupAuth *auth) switch (auth->type) { case SOUP_AUTH_DIGEST: return ((SoupAuthDigest *) auth)->stale; + case SOUP_AUTH_NTLM: + return TRUE; case SOUP_AUTH_BASIC: default: return FALSE; diff --git a/libsoup/soup-ntlm.c b/libsoup/soup-ntlm.c new file mode 100644 index 0000000..1979745 --- /dev/null +++ b/libsoup/soup-ntlm.c @@ -0,0 +1,1020 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * soup-ntlm.c: Microsoft Windows NTLM authentication support + * + * Authors: + * Dan Winship (danw@ximian.com) + * + * Public domain DES implementation from Phil Karn. + * + * All else Copyright (C) 2001, Ximian, Inc. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "soup-ntlm.h" + +#include +#include + +/* Base64 */ +static int base64_encode_close (unsigned char *in, + int inlen, + gboolean break_lines, + unsigned char *out, + int *state, + int *save); + +static int base64_encode_step (unsigned char *in, + int len, + gboolean break_lines, + unsigned char *out, + int *state, + int *save); + +static int base64_decode_step (unsigned char *in, + int len, + unsigned char *out, + int *state, + unsigned int *save); + +/* MD4 */ +static void md4sum (const unsigned char *in, + int nbytes, + unsigned char digest[16]); + +/* DES */ +typedef unsigned long DES_KS[16][2]; /* Single-key DES key schedule */ + +static void deskey (DES_KS, unsigned char *, int); + +static void des (DES_KS, unsigned char *); + +static void setup_schedule (const guchar *key_56, DES_KS ks); + +static void calc_response (const guchar *key, + const guchar *plaintext, + guchar *results); + +#define LM_PASSWORD_MAGIC "\x4B\x47\x53\x21\x40\x23\x24\x25" \ + "\x4B\x47\x53\x21\x40\x23\x24\x25" \ + "\x00\x00\x00\x00\x00" + +void +soup_ntlm_lanmanager_hash (const char *password, char hash[21]) +{ + guchar lm_password [14]; + DES_KS ks; + int i; + + for (i = 0; i < 14 && password [i]; i++) + lm_password [i] = toupper ((unsigned char) password [i]); + + for (; i < 14; i++) + lm_password [i] = '\0'; + + memcpy (hash, LM_PASSWORD_MAGIC, 21); + + setup_schedule (lm_password, ks); + des (ks, hash); + + setup_schedule (lm_password + 7, ks); + des (ks, hash + 8); +} + +void +soup_ntlm_nt_hash (const char *password, char hash[21]) +{ + unsigned char *buf, *p; + + p = buf = g_malloc (strlen (password) * 2); + + while (*password) { + *p++ = *password++; + *p++ = '\0'; + } + + md4sum (buf, p - buf, hash); + memset (hash + 16, 0, 5); + + g_free (buf); +} + +typedef struct { + guint16 length; + guint16 length2; + guint16 offset; + guchar zero_pad[2]; +} NTLMString; + +#define NTLM_REQUEST_HEADER "NTLMSSP\x00\x01\x00\x00\x00\xB2\x03\x00\x00" + +typedef struct { + guchar header[16]; + NTLMString domain; + NTLMString host; +} NTLMRequest; + +#define NTLM_CHALLENGE_NONCE_OFFSET 24 +#define NTLM_CHALLENGE_NONCE_LENGTH 8 + +#define NTLM_RESPONSE_HEADER "NTLMSSP\x00\x03\x00\x00\x00" +#define NTLM_RESPONSE_FLAGS "\x82\x01" + +typedef struct { + guchar header[12]; + + NTLMString lm_resp; + NTLMString nt_resp; + NTLMString domain; + NTLMString user; + NTLMString host; + + guint16 msg_len; + guchar zero_pad[2]; + + guchar flags[2]; + guchar zero_pad2[2]; +} NTLMResponse; + +#if G_BYTE_ORDER == G_BIG_ENDIAN +#define LE16(x) (((x & 0xFF) << 8) | ((x >> 8) & 0xFF)) +#else +#define LE16(x) x +#endif + +static void +ntlm_set_string (NTLMString *string, int *offset, int len) +{ + string->offset = LE16 (*offset); + string->length = string->length2 = LE16 (len); + *offset += len; +} + +char * +soup_ntlm_request (const char *host, const char *domain) +{ + NTLMRequest req; + unsigned char *out, *p; + int hlen = strlen (host), dlen = strlen (domain), offset; + int state, save; + + memset (&req, 0, sizeof (req)); + memcpy (req.header, NTLM_REQUEST_HEADER, sizeof (req.header)); + + offset = sizeof (req); + ntlm_set_string (&req.host, &offset, hlen); + ntlm_set_string (&req.domain, &offset, dlen); + + out = g_malloc (((offset + 3) * 4) / 3 + 6); + strncpy (out, "NTLM ", 5); + p = out + 5; + + state = save = 0; + p += base64_encode_step ((guchar *) &req, + sizeof (req), + FALSE, + p, + &state, + &save); + p += base64_encode_step ((guchar *) host, + hlen, + FALSE, + p, + &state, + &save); + p += base64_encode_close ((guchar *) domain, + dlen, + FALSE, + p, + &state, + &save); + *p = '\0'; + + return out; +} + +char * +soup_ntlm_response (const char *challenge, + const char *user, + const char *lm_hash, + const char *nt_hash, + const char *host, + const char *domain) +{ + int hlen = strlen (host), dlen = strlen (domain); + int ulen = strlen (user), offset, decodelen; + guchar lm_resp[24], nt_resp[24], *nonce; + NTLMResponse resp; + char *chall; + unsigned char *out, *p; + int state, save; + + if (strncmp (challenge, "NTLM ", 5) != 0) + return NULL; + + decodelen = strlen (challenge); + chall = g_malloc (decodelen); + + state = save = 0; + base64_decode_step ((guchar *) challenge + 5, + decodelen, + chall, + &state, + &save); + + nonce = chall + NTLM_CHALLENGE_NONCE_OFFSET; + nonce [NTLM_CHALLENGE_NONCE_LENGTH] = '\0'; + + calc_response (lm_hash, nonce, lm_resp); + calc_response (nt_hash, nonce, nt_resp); + g_free (chall); + + memset (&resp, 0, sizeof (resp)); + memcpy (resp.header, NTLM_RESPONSE_HEADER, sizeof (resp.header)); + memcpy (resp.flags, NTLM_RESPONSE_FLAGS, sizeof (resp.flags)); + + offset = sizeof (resp); + ntlm_set_string (&resp.domain, &offset, dlen); + ntlm_set_string (&resp.user, &offset, ulen); + ntlm_set_string (&resp.host, &offset, hlen); + ntlm_set_string (&resp.lm_resp, &offset, sizeof (lm_resp)); + ntlm_set_string (&resp.nt_resp, &offset, sizeof (nt_resp)); + + out = g_malloc (((offset + 3) * 4) / 3 + 6); + strncpy (out, "NTLM ", 5); + p = out + 5; + + state = save = 0; + + p += base64_encode_step ((guchar *) &resp, + sizeof (resp), + FALSE, + p, + &state, + &save); + p += base64_encode_step ((guchar *) domain, + dlen, + FALSE, + p, + &state, + &save); + p += base64_encode_step ((guchar *) user, + ulen, + FALSE, + p, + &state, + &save); + p += base64_encode_step ((guchar *) host, + hlen, + FALSE, + p, + &state, + &save); + p += base64_encode_step (lm_resp, + sizeof (lm_resp), + FALSE, + p, + &state, + &save); + p += base64_encode_close (nt_resp, + sizeof (nt_resp), + FALSE, + p, + &state, + &save); + *p = '\0'; + + return out; +} + +#define KEYBITS(k,s) \ + (((k[(s)/8] << ((s)%8)) & 0xFF) | (k[(s)/8+1] >> (8-(s)%8))) + +/* DES utils */ +/* Set up a key schedule based on a 56bit key */ +static void +setup_schedule (const guchar *key_56, DES_KS ks) +{ + guchar key[8]; + int i, c, bit; + + for (i = 0; i < 8; i++) { + key [i] = KEYBITS (key_56, i * 7); + + /* Fix parity */ + for (c = bit = 0; bit < 8; bit++) + if (key [i] & (1 << bit)) + c++; + if (!(c & 1)) + key [i] ^= 0x01; + } + + deskey (ks, key, 0); +} + +static void +calc_response (const guchar *key, const guchar *plaintext, guchar *results) +{ + DES_KS ks; + + memcpy (results, plaintext, 8); + memcpy (results + 8, plaintext, 8); + memcpy (results + 16, plaintext, 8); + + setup_schedule (key, ks); + des (ks, results); + + setup_schedule (key + 7, ks); + des (ks, results + 8); + + setup_schedule (key + 14, ks); + des (ks, results + 16); +} + +/* Base64 utils (straight from camel-mime-utils.c) */ +#define d(x) + +static char *base64_alphabet = +"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static unsigned char camel_mime_base64_rank[256] = { + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255, 0,255,255, + 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255, + 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, + 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, +}; + +/* + * call this when finished encoding everything, to + * flush off the last little bit + */ +static int +base64_encode_close (unsigned char *in, + int inlen, + gboolean break_lines, + unsigned char *out, + int *state, + int *save) +{ + int c1, c2; + unsigned char *outptr = out; + + if (inlen > 0) + outptr += base64_encode_step (in, + inlen, + break_lines, + outptr, + state, + save); + + c1 = ((unsigned char *) save) [1]; + c2 = ((unsigned char *) save) [2]; + + d(printf("mode = %d\nc1 = %c\nc2 = %c\n", + (int)((char *) save) [0], + (int)((char *) save) [1], + (int)((char *) save) [2])); + + switch (((char *) save) [0]) { + case 2: + outptr [2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ]; + g_assert (outptr [2] != 0); + goto skip; + case 1: + outptr[2] = '='; + skip: + outptr [0] = base64_alphabet [ c1 >> 2 ]; + outptr [1] = base64_alphabet [ c2 >> 4 | ( (c1&0x3) << 4 )]; + outptr [3] = '='; + outptr += 4; + break; + } + if (break_lines) + *outptr++ = '\n'; + + *save = 0; + *state = 0; + + return outptr-out; +} + +/* + * performs an 'encode step', only encodes blocks of 3 characters to the + * output at a time, saves left-over state in state and save (initialise to + * 0 on first invocation). + */ +static int +base64_encode_step(unsigned char *in, + int len, + gboolean break_lines, + unsigned char *out, + int *state, + int *save) +{ + register unsigned char *inptr, *outptr; + + if (len <= 0) + return 0; + + inptr = in; + outptr = out; + + d (printf ("we have %d chars, and %d saved chars\n", + len, + ((char *) save) [0])); + + if (len + ((char *) save) [0] > 2) { + unsigned char *inend = in+len-2; + register int c1, c2, c3; + register int already; + + already = *state; + + switch (((char *) save) [0]) { + case 1: c1 = ((unsigned char *) save) [1]; goto skip1; + case 2: c1 = ((unsigned char *) save) [1]; + c2 = ((unsigned char *) save) [2]; goto skip2; + } + + /* + * yes, we jump into the loop, no i'm not going to change it, + * it's beautiful! + */ + while (inptr < inend) { + c1 = *inptr++; + skip1: + c2 = *inptr++; + skip2: + c3 = *inptr++; + *outptr++ = base64_alphabet [ c1 >> 2 ]; + *outptr++ = base64_alphabet [ c2 >> 4 | + ((c1&0x3) << 4) ]; + *outptr++ = base64_alphabet [ ((c2 &0x0f) << 2) | + (c3 >> 6) ]; + *outptr++ = base64_alphabet [ c3 & 0x3f ]; + /* this is a bit ugly ... */ + if (break_lines && (++already)>=19) { + *outptr++='\n'; + already = 0; + } + } + + ((char *)save)[0] = 0; + len = 2-(inptr-inend); + *state = already; + } + + d(printf("state = %d, len = %d\n", + (int)((char *)save)[0], + len)); + + if (len>0) { + register char *saveout; + + /* points to the slot for the next char to save */ + saveout = & (((char *)save)[1]) + ((char *)save)[0]; + + /* len can only be 0 1 or 2 */ + switch(len) { + case 2: *saveout++ = *inptr++; + case 1: *saveout++ = *inptr++; + } + ((char *)save)[0]+=len; + } + + d(printf("mode = %d\nc1 = %c\nc2 = %c\n", + (int)((char *)save)[0], + (int)((char *)save)[1], + (int)((char *)save)[2])); + + return outptr-out; +} + + +/** + * base64_decode_step: decode a chunk of base64 encoded data + * @in: input stream + * @len: max length of data to decode + * @out: output stream + * @state: holds the number of bits that are stored in @save + * @save: leftover bits that have not yet been decoded + * + * Decodes a chunk of base64 encoded data + **/ +static int +base64_decode_step (unsigned char *in, + int len, + unsigned char *out, + int *state, + unsigned int *save) +{ + register unsigned char *inptr, *outptr; + unsigned char *inend, c; + register unsigned int v; + int i; + + inend = in+len; + outptr = out; + + /* convert 4 base64 bytes to 3 normal bytes */ + v=*save; + i=*state; + inptr = in; + while (inptr < inend) { + c = camel_mime_base64_rank [*inptr++]; + if (c != 0xff) { + v = (v<<6) | c; + i++; + if (i==4) { + *outptr++ = v>>16; + *outptr++ = v>>8; + *outptr++ = v; + i=0; + } + } + } + + *save = v; + *state = i; + + /* quick scan back for '=' on the end somewhere */ + /* fortunately we can drop 1 output char for each trailing = (upto 2) */ + i=2; + while (inptr > in && i) { + inptr--; + if (camel_mime_base64_rank [*inptr] != 0xff) { + if (*inptr == '=') + outptr--; + i--; + } + } + + /* if i!= 0 then there is a truncation error! */ + return outptr - out; +} + + +/* + * MD4 encoder. (The one everyone else uses is not GPL-compatible; + * this is a reimplementation from spec.) This doesn't need to be + * efficient for our purposes, although it would be nice to fix + * it to not malloc()... + */ + +#define F(X,Y,Z) ( ((X)&(Y)) | ((~(X))&(Z)) ) +#define G(X,Y,Z) ( ((X)&(Y)) | ((X)&(Z)) | ((Y)&(Z)) ) +#define H(X,Y,Z) ( (X)^(Y)^(Z) ) +#define ROT(val, n) ( ((val) << (n)) | ((val) >> (32 - (n))) ) + +static void +md4sum (const unsigned char *in, int nbytes, unsigned char digest[16]) +{ + unsigned char *M; + guint32 A, B, C, D, AA, BB, CC, DD, X[16]; + int pbytes, nbits = nbytes * 8, i, j; + + pbytes = (120 - (nbytes % 64)) % 64; + M = alloca (nbytes + pbytes + 8); + memcpy (M, in, nbytes); + memset (M + nbytes, 0, pbytes + 8); + M[nbytes] = 0x80; + M[nbytes + pbytes] = nbits & 0xFF; + M[nbytes + pbytes + 1] = (nbits >> 8) & 0xFF; + M[nbytes + pbytes + 2] = (nbits >> 16) & 0xFF; + M[nbytes + pbytes + 3] = (nbits >> 24) & 0xFF; + + A = 0x67452301; + B = 0xEFCDAB89; + C = 0x98BADCFE; + D = 0x10325476; + + for (i = 0; i < nbytes + pbytes + 8; i += 64) { + for (j = 0; j < 16; j++) { + X[j] = (M[i + j*4]) | + (M[i + j*4 + 1] << 8) | + (M[i + j*4 + 2] << 16) | + (M[i + j*4 + 3] << 24); + } + + AA = A; + BB = B; + CC = C; + DD = D; + + A = ROT (A + F(B, C, D) + X[0], 3); + D = ROT (D + F(A, B, C) + X[1], 7); + C = ROT (C + F(D, A, B) + X[2], 11); + B = ROT (B + F(C, D, A) + X[3], 19); + A = ROT (A + F(B, C, D) + X[4], 3); + D = ROT (D + F(A, B, C) + X[5], 7); + C = ROT (C + F(D, A, B) + X[6], 11); + B = ROT (B + F(C, D, A) + X[7], 19); + A = ROT (A + F(B, C, D) + X[8], 3); + D = ROT (D + F(A, B, C) + X[9], 7); + C = ROT (C + F(D, A, B) + X[10], 11); + B = ROT (B + F(C, D, A) + X[11], 19); + A = ROT (A + F(B, C, D) + X[12], 3); + D = ROT (D + F(A, B, C) + X[13], 7); + C = ROT (C + F(D, A, B) + X[14], 11); + B = ROT (B + F(C, D, A) + X[15], 19); + + A = ROT (A + G(B, C, D) + X[0] + 0x5A827999, 3); + D = ROT (D + G(A, B, C) + X[4] + 0x5A827999, 5); + C = ROT (C + G(D, A, B) + X[8] + 0x5A827999, 9); + B = ROT (B + G(C, D, A) + X[12] + 0x5A827999, 13); + A = ROT (A + G(B, C, D) + X[1] + 0x5A827999, 3); + D = ROT (D + G(A, B, C) + X[5] + 0x5A827999, 5); + C = ROT (C + G(D, A, B) + X[9] + 0x5A827999, 9); + B = ROT (B + G(C, D, A) + X[13] + 0x5A827999, 13); + A = ROT (A + G(B, C, D) + X[2] + 0x5A827999, 3); + D = ROT (D + G(A, B, C) + X[6] + 0x5A827999, 5); + C = ROT (C + G(D, A, B) + X[10] + 0x5A827999, 9); + B = ROT (B + G(C, D, A) + X[14] + 0x5A827999, 13); + A = ROT (A + G(B, C, D) + X[3] + 0x5A827999, 3); + D = ROT (D + G(A, B, C) + X[7] + 0x5A827999, 5); + C = ROT (C + G(D, A, B) + X[11] + 0x5A827999, 9); + B = ROT (B + G(C, D, A) + X[15] + 0x5A827999, 13); + + A = ROT (A + H(B, C, D) + X[0] + 0x6ED9EBA1, 3); + D = ROT (D + H(A, B, C) + X[8] + 0x6ED9EBA1, 9); + C = ROT (C + H(D, A, B) + X[4] + 0x6ED9EBA1, 11); + B = ROT (B + H(C, D, A) + X[12] + 0x6ED9EBA1, 15); + A = ROT (A + H(B, C, D) + X[2] + 0x6ED9EBA1, 3); + D = ROT (D + H(A, B, C) + X[10] + 0x6ED9EBA1, 9); + C = ROT (C + H(D, A, B) + X[6] + 0x6ED9EBA1, 11); + B = ROT (B + H(C, D, A) + X[14] + 0x6ED9EBA1, 15); + A = ROT (A + H(B, C, D) + X[1] + 0x6ED9EBA1, 3); + D = ROT (D + H(A, B, C) + X[9] + 0x6ED9EBA1, 9); + C = ROT (C + H(D, A, B) + X[5] + 0x6ED9EBA1, 11); + B = ROT (B + H(C, D, A) + X[13] + 0x6ED9EBA1, 15); + A = ROT (A + H(B, C, D) + X[3] + 0x6ED9EBA1, 3); + D = ROT (D + H(A, B, C) + X[11] + 0x6ED9EBA1, 9); + C = ROT (C + H(D, A, B) + X[7] + 0x6ED9EBA1, 11); + B = ROT (B + H(C, D, A) + X[15] + 0x6ED9EBA1, 15); + + A += AA; + B += BB; + C += CC; + D += DD; + } + + digest[0] = A & 0xFF; + digest[1] = (A >> 8) & 0xFF; + digest[2] = (A >> 16) & 0xFF; + digest[3] = (A >> 24) & 0xFF; + digest[4] = B & 0xFF; + digest[5] = (B >> 8) & 0xFF; + digest[6] = (B >> 16) & 0xFF; + digest[7] = (B >> 24) & 0xFF; + digest[8] = C & 0xFF; + digest[9] = (C >> 8) & 0xFF; + digest[10] = (C >> 16) & 0xFF; + digest[11] = (C >> 24) & 0xFF; + digest[12] = D & 0xFF; + digest[13] = (D >> 8) & 0xFF; + digest[14] = (D >> 16) & 0xFF; + digest[15] = (D >> 24) & 0xFF; +} + + +/* Public domain DES implementation from Phil Karn */ +static unsigned long Spbox[8][64] = { +0x01010400,0x00000000,0x00010000,0x01010404, +0x01010004,0x00010404,0x00000004,0x00010000, +0x00000400,0x01010400,0x01010404,0x00000400, +0x01000404,0x01010004,0x01000000,0x00000004, +0x00000404,0x01000400,0x01000400,0x00010400, +0x00010400,0x01010000,0x01010000,0x01000404, +0x00010004,0x01000004,0x01000004,0x00010004, +0x00000000,0x00000404,0x00010404,0x01000000, +0x00010000,0x01010404,0x00000004,0x01010000, +0x01010400,0x01000000,0x01000000,0x00000400, +0x01010004,0x00010000,0x00010400,0x01000004, +0x00000400,0x00000004,0x01000404,0x00010404, +0x01010404,0x00010004,0x01010000,0x01000404, +0x01000004,0x00000404,0x00010404,0x01010400, +0x00000404,0x01000400,0x01000400,0x00000000, +0x00010004,0x00010400,0x00000000,0x01010004, +0x80108020,0x80008000,0x00008000,0x00108020, +0x00100000,0x00000020,0x80100020,0x80008020, +0x80000020,0x80108020,0x80108000,0x80000000, +0x80008000,0x00100000,0x00000020,0x80100020, +0x00108000,0x00100020,0x80008020,0x00000000, +0x80000000,0x00008000,0x00108020,0x80100000, +0x00100020,0x80000020,0x00000000,0x00108000, +0x00008020,0x80108000,0x80100000,0x00008020, +0x00000000,0x00108020,0x80100020,0x00100000, +0x80008020,0x80100000,0x80108000,0x00008000, +0x80100000,0x80008000,0x00000020,0x80108020, +0x00108020,0x00000020,0x00008000,0x80000000, +0x00008020,0x80108000,0x00100000,0x80000020, +0x00100020,0x80008020,0x80000020,0x00100020, +0x00108000,0x00000000,0x80008000,0x00008020, +0x80000000,0x80100020,0x80108020,0x00108000, +0x00000208,0x08020200,0x00000000,0x08020008, +0x08000200,0x00000000,0x00020208,0x08000200, +0x00020008,0x08000008,0x08000008,0x00020000, +0x08020208,0x00020008,0x08020000,0x00000208, +0x08000000,0x00000008,0x08020200,0x00000200, +0x00020200,0x08020000,0x08020008,0x00020208, +0x08000208,0x00020200,0x00020000,0x08000208, +0x00000008,0x08020208,0x00000200,0x08000000, +0x08020200,0x08000000,0x00020008,0x00000208, +0x00020000,0x08020200,0x08000200,0x00000000, +0x00000200,0x00020008,0x08020208,0x08000200, +0x08000008,0x00000200,0x00000000,0x08020008, +0x08000208,0x00020000,0x08000000,0x08020208, +0x00000008,0x00020208,0x00020200,0x08000008, +0x08020000,0x08000208,0x00000208,0x08020000, +0x00020208,0x00000008,0x08020008,0x00020200, +0x00802001,0x00002081,0x00002081,0x00000080, +0x00802080,0x00800081,0x00800001,0x00002001, +0x00000000,0x00802000,0x00802000,0x00802081, +0x00000081,0x00000000,0x00800080,0x00800001, +0x00000001,0x00002000,0x00800000,0x00802001, +0x00000080,0x00800000,0x00002001,0x00002080, +0x00800081,0x00000001,0x00002080,0x00800080, +0x00002000,0x00802080,0x00802081,0x00000081, +0x00800080,0x00800001,0x00802000,0x00802081, +0x00000081,0x00000000,0x00000000,0x00802000, +0x00002080,0x00800080,0x00800081,0x00000001, +0x00802001,0x00002081,0x00002081,0x00000080, +0x00802081,0x00000081,0x00000001,0x00002000, +0x00800001,0x00002001,0x00802080,0x00800081, +0x00002001,0x00002080,0x00800000,0x00802001, +0x00000080,0x00800000,0x00002000,0x00802080, +0x00000100,0x02080100,0x02080000,0x42000100, +0x00080000,0x00000100,0x40000000,0x02080000, +0x40080100,0x00080000,0x02000100,0x40080100, +0x42000100,0x42080000,0x00080100,0x40000000, +0x02000000,0x40080000,0x40080000,0x00000000, +0x40000100,0x42080100,0x42080100,0x02000100, +0x42080000,0x40000100,0x00000000,0x42000000, +0x02080100,0x02000000,0x42000000,0x00080100, +0x00080000,0x42000100,0x00000100,0x02000000, +0x40000000,0x02080000,0x42000100,0x40080100, +0x02000100,0x40000000,0x42080000,0x02080100, +0x40080100,0x00000100,0x02000000,0x42080000, +0x42080100,0x00080100,0x42000000,0x42080100, +0x02080000,0x00000000,0x40080000,0x42000000, +0x00080100,0x02000100,0x40000100,0x00080000, +0x00000000,0x40080000,0x02080100,0x40000100, +0x20000010,0x20400000,0x00004000,0x20404010, +0x20400000,0x00000010,0x20404010,0x00400000, +0x20004000,0x00404010,0x00400000,0x20000010, +0x00400010,0x20004000,0x20000000,0x00004010, +0x00000000,0x00400010,0x20004010,0x00004000, +0x00404000,0x20004010,0x00000010,0x20400010, +0x20400010,0x00000000,0x00404010,0x20404000, +0x00004010,0x00404000,0x20404000,0x20000000, +0x20004000,0x00000010,0x20400010,0x00404000, +0x20404010,0x00400000,0x00004010,0x20000010, +0x00400000,0x20004000,0x20000000,0x00004010, +0x20000010,0x20404010,0x00404000,0x20400000, +0x00404010,0x20404000,0x00000000,0x20400010, +0x00000010,0x00004000,0x20400000,0x00404010, +0x00004000,0x00400010,0x20004010,0x00000000, +0x20404000,0x20000000,0x00400010,0x20004010, +0x00200000,0x04200002,0x04000802,0x00000000, +0x00000800,0x04000802,0x00200802,0x04200800, +0x04200802,0x00200000,0x00000000,0x04000002, +0x00000002,0x04000000,0x04200002,0x00000802, +0x04000800,0x00200802,0x00200002,0x04000800, +0x04000002,0x04200000,0x04200800,0x00200002, +0x04200000,0x00000800,0x00000802,0x04200802, +0x00200800,0x00000002,0x04000000,0x00200800, +0x04000000,0x00200800,0x00200000,0x04000802, +0x04000802,0x04200002,0x04200002,0x00000002, +0x00200002,0x04000000,0x04000800,0x00200000, +0x04200800,0x00000802,0x00200802,0x04200800, +0x00000802,0x04000002,0x04200802,0x04200000, +0x00200800,0x00000000,0x00000002,0x04200802, +0x00000000,0x00200802,0x04200000,0x00000800, +0x04000002,0x04000800,0x00000800,0x00200002, +0x10001040,0x00001000,0x00040000,0x10041040, +0x10000000,0x10001040,0x00000040,0x10000000, +0x00040040,0x10040000,0x10041040,0x00041000, +0x10041000,0x00041040,0x00001000,0x00000040, +0x10040000,0x10000040,0x10001000,0x00001040, +0x00041000,0x00040040,0x10040040,0x10041000, +0x00001040,0x00000000,0x00000000,0x10040040, +0x10000040,0x10001000,0x00041040,0x00040000, +0x00041040,0x00040000,0x10041000,0x00001000, +0x00000040,0x10040040,0x00001000,0x00041040, +0x10001000,0x00000040,0x10000040,0x10040000, +0x10040040,0x10000000,0x00040000,0x10001040, +0x00000000,0x10041040,0x00040040,0x10000040, +0x10040000,0x10001000,0x10001040,0x00000000, +0x10041040,0x00041000,0x00041000,0x00001040, +0x00001040,0x00040040,0x10000000,0x10041000, +}; + +#undef F +#define F(l,r,key){\ + work = ((r >> 4) | (r << 28)) ^ key[0];\ + l ^= Spbox[6][work & 0x3f];\ + l ^= Spbox[4][(work >> 8) & 0x3f];\ + l ^= Spbox[2][(work >> 16) & 0x3f];\ + l ^= Spbox[0][(work >> 24) & 0x3f];\ + work = r ^ key[1];\ + l ^= Spbox[7][work & 0x3f];\ + l ^= Spbox[5][(work >> 8) & 0x3f];\ + l ^= Spbox[3][(work >> 16) & 0x3f];\ + l ^= Spbox[1][(work >> 24) & 0x3f];\ +} +/* Encrypt or decrypt a block of data in ECB mode */ +static void +des(ks,block) +unsigned long ks[16][2]; /* Key schedule */ +unsigned char block[8]; /* Data block */ +{ + unsigned long left,right,work; + + /* Read input block and place in left/right in big-endian order */ + left = ((unsigned long)block[0] << 24) + | ((unsigned long)block[1] << 16) + | ((unsigned long)block[2] << 8) + | (unsigned long)block[3]; + right = ((unsigned long)block[4] << 24) + | ((unsigned long)block[5] << 16) + | ((unsigned long)block[6] << 8) + | (unsigned long)block[7]; + + /* Hoey's clever initial permutation algorithm, from Outerbridge + * (see Schneier p 478) + * + * The convention here is the same as Outerbridge: rotate each + * register left by 1 bit, i.e., so that "left" contains permuted + * input bits 2, 3, 4, ... 1 and "right" contains 33, 34, 35, ... 32 + * (using origin-1 numbering as in the FIPS). This allows us to avoid + * one of the two rotates that would otherwise be required in each of + * the 16 rounds. + */ + work = ((left >> 4) ^ right) & 0x0f0f0f0f; + right ^= work; + left ^= work << 4; + work = ((left >> 16) ^ right) & 0xffff; + right ^= work; + left ^= work << 16; + work = ((right >> 2) ^ left) & 0x33333333; + left ^= work; + right ^= (work << 2); + work = ((right >> 8) ^ left) & 0xff00ff; + left ^= work; + right ^= (work << 8); + right = (right << 1) | (right >> 31); + work = (left ^ right) & 0xaaaaaaaa; + left ^= work; + right ^= work; + left = (left << 1) | (left >> 31); + + /* Now do the 16 rounds */ + F(left,right,ks[0]); + F(right,left,ks[1]); + F(left,right,ks[2]); + F(right,left,ks[3]); + F(left,right,ks[4]); + F(right,left,ks[5]); + F(left,right,ks[6]); + F(right,left,ks[7]); + F(left,right,ks[8]); + F(right,left,ks[9]); + F(left,right,ks[10]); + F(right,left,ks[11]); + F(left,right,ks[12]); + F(right,left,ks[13]); + F(left,right,ks[14]); + F(right,left,ks[15]); + + /* Inverse permutation, also from Hoey via Outerbridge and Schneier */ + right = (right << 31) | (right >> 1); + work = (left ^ right) & 0xaaaaaaaa; + left ^= work; + right ^= work; + left = (left >> 1) | (left << 31); + work = ((left >> 8) ^ right) & 0xff00ff; + right ^= work; + left ^= work << 8; + work = ((left >> 2) ^ right) & 0x33333333; + right ^= work; + left ^= work << 2; + work = ((right >> 16) ^ left) & 0xffff; + left ^= work; + right ^= work << 16; + work = ((right >> 4) ^ left) & 0x0f0f0f0f; + left ^= work; + right ^= work << 4; + + /* Put the block back into the user's buffer with final swap */ + block[0] = right >> 24; + block[1] = right >> 16; + block[2] = right >> 8; + block[3] = right; + block[4] = left >> 24; + block[5] = left >> 16; + block[6] = left >> 8; + block[7] = left; +} + +/* Key schedule-related tables from FIPS-46 */ + +/* permuted choice table (key) */ +static unsigned char pc1[] = { + 57, 49, 41, 33, 25, 17, 9, + 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, + 19, 11, 3, 60, 52, 44, 36, + + 63, 55, 47, 39, 31, 23, 15, + 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, + 21, 13, 5, 28, 20, 12, 4 +}; + +/* number left rotations of pc1 */ +static unsigned char totrot[] = { + 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 +}; + +/* permuted choice key (table) */ +static unsigned char pc2[] = { + 14, 17, 11, 24, 1, 5, + 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, + 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, + 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, + 46, 42, 50, 36, 29, 32 +}; + +/* End of DES-defined tables */ + + +/* bit 0 is left-most in byte */ +static int bytebit[] = { + 0200,0100,040,020,010,04,02,01 +}; + + +/* Generate key schedule for encryption or decryption + * depending on the value of "decrypt" + */ +static void +deskey(k,key,decrypt) +DES_KS k; /* Key schedule array */ +unsigned char *key; /* 64 bits (will use only 56) */ +int decrypt; /* 0 = encrypt, 1 = decrypt */ +{ + unsigned char pc1m[56]; /* place to modify pc1 into */ + unsigned char pcr[56]; /* place to rotate pc1 into */ + register int i,j,l; + int m; + unsigned char ks[8]; + + for (j=0; j<56; j++) { /* convert pc1 to bits of key */ + l=pc1[j]-1; /* integer bit location */ + m = l & 07; /* find bit */ + pc1m[j]=(key[l>>3] & /* find which key byte l is in */ + bytebit[m]) /* and which bit of that byte */ + ? 1 : 0; /* and store 1-bit result */ + } + for (i=0; i<16; i++) { /* key chunk for each iteration */ + memset(ks,0,sizeof(ks)); /* Clear key schedule */ + for (j=0; j<56; j++) /* rotate pc1 the right amount */ + pcr[j] = pc1m[(l=j+totrot[decrypt? 15-i : i])<(j<28? 28 : 56) ? l: l-28]; + /* rotate left and right halves independently */ + for (j=0; j<48; j++){ /* select bits individually */ + /* check bit that goes to ks[j] */ + if (pcr[pc2[j]-1]){ + /* mask it in if it's there */ + l= j % 6; + ks[j/6] |= bytebit[l] >> 2; + } + } + /* Now convert to packed odd/even interleaved form */ + k[i][0] = ((long)ks[0] << 24) + | ((long)ks[2] << 16) + | ((long)ks[4] << 8) + | ((long)ks[6]); + k[i][1] = ((long)ks[1] << 24) + | ((long)ks[3] << 16) + | ((long)ks[5] << 8) + | ((long)ks[7]); + } +} diff --git a/libsoup/soup-ntlm.h b/libsoup/soup-ntlm.h new file mode 100644 index 0000000..1f4eea2 --- /dev/null +++ b/libsoup/soup-ntlm.h @@ -0,0 +1,32 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * soup-ntlm.h: Microsoft Windows NTLM authentication support + * + * Authors: + * Dan Winship (danw@ximian.com) + * + * Copyright (C) 2001, Ximian, Inc. + */ + +#ifndef NTLM_H +#define NTLM_H 1 + +#include + +void soup_ntlm_lanmanager_hash (const char *password, + char hash[21]); + +void soup_ntlm_nt_hash (const char *password, + char hash[21]); + +char *soup_ntlm_request (const char *host, + const char *domain); + +char *soup_ntlm_response (const char *challenge, + const char *user, + const char *lm_hash, + const char *nt_hash, + const char *host, + const char *domain); + +#endif /* NTLM_H */ diff --git a/libsoup/soup-server.h b/libsoup/soup-server.h index 818bcd2..7813048 100644 --- a/libsoup/soup-server.h +++ b/libsoup/soup-server.h @@ -16,35 +16,40 @@ #include "soup-message.h" typedef enum { - SOUP_AUTH_TYPE_BASIC = 1, - SOUP_AUTH_TYPE_DIGEST, - SOUP_AUTH_TYPE_ANONYMOUS, - SOUP_AUTH_TYPE_DENY + SOUP_AUTH_TYPE_BASIC = (1 << 0), + SOUP_AUTH_TYPE_DIGEST = (1 << 1), + SOUP_AUTH_TYPE_NTLM = (1 << 2), + SOUP_AUTH_TYPE_DENY = (1 << 3) } SoupServerAuthType; typedef struct { SoupServerAuthType type; + const gchar *realm; const gchar *username; const gchar *password; } SoupServerBasicToken; typedef struct { SoupServerAuthType type; + const gchar *realm; const gchar *username; const gchar *password_hash; - const gchar *realm; } SoupServerDigestToken; typedef struct { SoupServerAuthType type; - const gchar *email; -} SoupServerAnonymousToken; + const gchar *host; + const gchar *domain; + const gchar *user; + const gchar *lm_hash; + const gchar *nt_hash; +} SoupServerNTLMToken; typedef union { - SoupServerAuthType type; - SoupServerBasicToken basic; - SoupServerDigestToken digest; - SoupServerAnonymousToken anonymous; + SoupServerAuthType type; + SoupServerBasicToken basic; + SoupServerDigestToken digest; + SoupServerNTLMToken ntlm; } SoupServerAuthToken; typedef gboolean (*SoupServerAuthorizeFn) (SoupMessage *msg, @@ -75,6 +80,9 @@ void soup_server_register_full (const gchar *methodname, void soup_server_unregister (const gchar *methodname); +void soup_server_set_unknown_path_handler (SoupServerCallbackFn cb, + gpointer user_data); + /* CGI Server methods */ void soup_server_main (void); -- 2.7.4