From fe50ca8f54ded2e119bde08831785fbe0db2ee99 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Thu, 16 Jan 2003 03:29:54 +0000 Subject: [PATCH] Updates to the NTLMSSP code again - moving the base64 decode fuctionality out of the SWAT code, and adding a base64 encoder. The main purpose of this patch is to add NTLMSSP support to 'ntlm_auth', for use with Squid. Unfortunetly the squid side doesn't quite support what we need yet. Changes to winbind to get us the info we need, and a couple of consequential changes/cleanups in the rest of the code. Andrew Bartlett --- source/auth/auth_ntlmssp.c | 9 +- source/include/ntlmssp.h | 6 +- source/lib/util_str.c | 94 +++++++++++++++++ source/nsswitch/winbindd.c | 1 + source/nsswitch/winbindd_misc.c | 12 +++ source/nsswitch/winbindd_nss.h | 4 +- source/smbd/sesssetup.c | 9 +- source/utils/ntlm_auth.c | 179 ++++++++++++++++++++++++++------ source/web/cgi.c | 31 ------ 9 files changed, 273 insertions(+), 72 deletions(-) diff --git a/source/auth/auth_ntlmssp.c b/source/auth/auth_ntlmssp.c index ab594c19e3d..3e650a7a2a7 100644 --- a/source/auth/auth_ntlmssp.c +++ b/source/auth/auth_ntlmssp.c @@ -23,15 +23,15 @@ #include "includes.h" -static const uint8 *auth_ntlmssp_get_challenge(void *cookie) +static const uint8 *auth_ntlmssp_get_challenge(struct ntlmssp_state *ntlmssp_state) { - AUTH_NTLMSSP_STATE *auth_ntlmssp_state = cookie; + AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context; return auth_ntlmssp_state->auth_context->get_ntlm_challenge(auth_ntlmssp_state->auth_context); } -static NTSTATUS auth_ntlmssp_check_password(void *cookie) +static NTSTATUS auth_ntlmssp_check_password(struct ntlmssp_state *ntlmssp_state) { - AUTH_NTLMSSP_STATE *auth_ntlmssp_state = cookie; + AUTH_NTLMSSP_STATE *auth_ntlmssp_state = ntlmssp_state->auth_context; uint32 auth_flags = AUTH_FLAG_NONE; auth_usersupplied_info *user_info = NULL; DATA_BLOB plaintext_password = data_blob(NULL, 0); @@ -107,6 +107,7 @@ NTSTATUS auth_ntlmssp_start(AUTH_NTLMSSP_STATE **auth_ntlmssp_state) (*auth_ntlmssp_state)->ntlmssp_state->auth_context = (*auth_ntlmssp_state); (*auth_ntlmssp_state)->ntlmssp_state->get_challenge = auth_ntlmssp_get_challenge; (*auth_ntlmssp_state)->ntlmssp_state->check_password = auth_ntlmssp_check_password; + (*auth_ntlmssp_state)->ntlmssp_state->server_role = lp_server_role(); return NT_STATUS_OK; } diff --git a/source/include/ntlmssp.h b/source/include/ntlmssp.h index a8fe345cc5f..79d0446a771 100644 --- a/source/include/ntlmssp.h +++ b/source/include/ntlmssp.h @@ -79,10 +79,12 @@ typedef struct ntlmssp_state DATA_BLOB nt_resp; DATA_BLOB chal; void *auth_context; - const uint8 *(*get_challenge)(void *auth_context); - NTSTATUS (*check_password)(void *auth_context); + const uint8 *(*get_challenge)(struct ntlmssp_state *ntlmssp_state); + NTSTATUS (*check_password)(struct ntlmssp_state *ntlmssp_state); const char *(*get_global_myname)(void); const char *(*get_domain)(void); + + int server_role; } NTLMSSP_STATE; diff --git a/source/lib/util_str.c b/source/lib/util_str.c index 148181fdddc..2224a24ab31 100644 --- a/source/lib/util_str.c +++ b/source/lib/util_str.c @@ -1531,6 +1531,100 @@ void rfc1738_unescape(char *buf) } } +static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/*************************************************************************** +decode a base64 string into a DATA_BLOB - simple and slow algorithm + ***************************************************************************/ +DATA_BLOB base64_decode_data_blob(const char *s) +{ + int bit_offset, byte_offset, idx, i, n; + DATA_BLOB decoded = data_blob(s, strlen(s)+1); + unsigned char *d = decoded.data; + char *p; + + n=i=0; + + while (*s && (p=strchr_m(b64,*s))) { + idx = (int)(p - b64); + byte_offset = (i*6)/8; + bit_offset = (i*6)%8; + d[byte_offset] &= ~((1<<(8-bit_offset))-1); + if (bit_offset < 3) { + d[byte_offset] |= (idx << (2-bit_offset)); + n = byte_offset+1; + } else { + d[byte_offset] |= (idx >> (bit_offset-2)); + d[byte_offset+1] = 0; + d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF; + n = byte_offset+2; + } + s++; i++; + } + + /* fix up length */ + decoded.length = n; + return decoded; +} + +/*************************************************************************** +decode a base64 string in-place - wrapper for the above +***************************************************************************/ +void base64_decode(char *s) +{ + DATA_BLOB decoded = base64_decode_data_blob(s); + memcpy(s, decoded.data, decoded.length); + data_blob_free(&decoded); + + /* null terminate */ + s[decoded.length] = '\0'; +} + +/*************************************************************************** +encode a base64 string into a malloc()ed string caller to free. + +From SQUID: adopted from http://ftp.sunet.se/pub2/gnu/vm/base64-encode.c with adjustments +***************************************************************************/ +char * base64_encode_data_blob(DATA_BLOB data) +{ + int bits = 0; + int char_count = 0; + int out_cnt = 0; + size_t len = data.length; + size_t output_len = data.length * 2; + char *result = malloc(output_len); /* get us plenty of space */ + + while (len-- && out_cnt < (data.length * 2) - 5) { + int c = (unsigned char) *(data.data++); + bits += c; + char_count++; + if (char_count == 3) { + result[out_cnt++] = b64[bits >> 18]; + result[out_cnt++] = b64[(bits >> 12) & 0x3f]; + result[out_cnt++] = b64[(bits >> 6) & 0x3f]; + result[out_cnt++] = b64[bits & 0x3f]; + bits = 0; + char_count = 0; + } else { + bits <<= 8; + } + } + if (char_count != 0) { + bits <<= 16 - (8 * char_count); + result[out_cnt++] = b64[bits >> 18]; + result[out_cnt++] = b64[(bits >> 12) & 0x3f]; + if (char_count == 1) { + result[out_cnt++] = '='; + result[out_cnt++] = '='; + } else { + result[out_cnt++] = b64[(bits >> 6) & 0x3f]; + result[out_cnt++] = '='; + } + } + result[out_cnt] = '\0'; /* terminate */ + return result; +} + #ifdef VALGRIND size_t valgrind_strlen(const char *s) { diff --git a/source/nsswitch/winbindd.c b/source/nsswitch/winbindd.c index 815d6914853..c99bb39f7d5 100644 --- a/source/nsswitch/winbindd.c +++ b/source/nsswitch/winbindd.c @@ -264,6 +264,7 @@ static struct dispatch_table dispatch_table[] = { { WINBINDD_INFO, winbindd_info, "INFO" }, { WINBINDD_INTERFACE_VERSION, winbindd_interface_version, "INTERFACE_VERSION" }, { WINBINDD_DOMAIN_NAME, winbindd_domain_name, "DOMAIN_NAME" }, + { WINBINDD_NETBIOS_NAME, winbindd_netbios_name, "NETBIOS_NAME" }, /* WINS functions */ diff --git a/source/nsswitch/winbindd_misc.c b/source/nsswitch/winbindd_misc.c index 6b7ef382ddf..0b283812b21 100644 --- a/source/nsswitch/winbindd_misc.c +++ b/source/nsswitch/winbindd_misc.c @@ -221,3 +221,15 @@ enum winbindd_result winbindd_domain_name(struct winbindd_cli_state *state) return WINBINDD_OK; } + +/* What's my name again? */ + +enum winbindd_result winbindd_netbios_name(struct winbindd_cli_state *state) +{ + + DEBUG(3, ("[%5d]: request netbios name\n", state->pid)); + + fstrcpy(state->response.data.netbios_name, global_myname()); + + return WINBINDD_OK; +} diff --git a/source/nsswitch/winbindd_nss.h b/source/nsswitch/winbindd_nss.h index 5c2db2ac2c0..2c87a771009 100644 --- a/source/nsswitch/winbindd_nss.h +++ b/source/nsswitch/winbindd_nss.h @@ -36,7 +36,7 @@ /* Update this when you change the interface. */ -#define WINBIND_INTERFACE_VERSION 6 +#define WINBIND_INTERFACE_VERSION 7 /* Socket commands */ @@ -102,6 +102,7 @@ enum winbindd_cmd { /* this is like GETGRENT but gives an empty group list */ WINBINDD_GETGRLST, + WINBINDD_NETBIOS_NAME, /* The netbios name of the server */ /* Placeholder for end of cmd list */ WINBINDD_NUM_CMDS @@ -221,6 +222,7 @@ struct winbindd_response { fstring samba_version; } info; fstring domain_name; + fstring netbios_name; struct auth_reply { uint32 nt_status; diff --git a/source/smbd/sesssetup.c b/source/smbd/sesssetup.c index 939cdf2a913..9dc9bee0417 100644 --- a/source/smbd/sesssetup.c +++ b/source/smbd/sesssetup.c @@ -361,14 +361,12 @@ static int reply_spnego_negotiate(connection_struct *conn, reply_spnego_ntlmssp_ok(conn, outbuf, global_ntlmssp_state); auth_ntlmssp_end(&global_ntlmssp_state); - data_blob_free(&chal); /* and tell smbd that we have already replied to this packet */ return -1; } auth_ntlmssp_end(&global_ntlmssp_state); - data_blob_free(&chal); return ERROR_NT(nt_status_squash(nt_status)); } @@ -392,15 +390,15 @@ static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf, } nt_status = auth_ntlmssp_update(global_ntlmssp_state, - auth, &auth_reply); + auth, &auth_reply); data_blob_free(&auth); - data_blob_free(&auth_reply); if (NT_STATUS_IS_OK(nt_status)) { reply_spnego_ntlmssp_ok(conn, outbuf, global_ntlmssp_state); auth_ntlmssp_end(&global_ntlmssp_state); + data_blob_free(&auth_reply); } else { /* !NT_STATUS_IS_OK(nt_status) */ auth_ntlmssp_end(&global_ntlmssp_state); @@ -450,7 +448,8 @@ static int reply_spnego_anonymous(connection_struct *conn, char *inbuf, char *ou /**************************************************************************** reply to a session setup command ****************************************************************************/ -static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf,char *outbuf, +static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf, + char *outbuf, int length,int bufsize) { uint8 *p; diff --git a/source/utils/ntlm_auth.c b/source/utils/ntlm_auth.c index 2eb7f1a96f2..b8cb0f2478c 100644 --- a/source/utils/ntlm_auth.c +++ b/source/utils/ntlm_auth.c @@ -4,7 +4,7 @@ Winbind status program. Copyright (C) Tim Potter 2000-2002 - Copyright (C) Andrew Bartlett 2002 + Copyright (C) Andrew Bartlett 2003 Copyright (C) Francesco Chemolli 2000 This program is free software; you can redistribute it and/or modify @@ -30,8 +30,9 @@ #define SQUID_BUFFER_SIZE 2010 enum squid_mode { + SQUID_2_4_BASIC, SQUID_2_5_BASIC, - SQUID_2_4_BASIC + SQUID_2_5_NTLMSSP }; @@ -88,6 +89,9 @@ static const char *get_winbind_domain(void) struct winbindd_response response; static fstring winbind_domain; + if (*winbind_domain) { + return winbind_domain; + } ZERO_STRUCT(response); @@ -105,6 +109,32 @@ static const char *get_winbind_domain(void) } +static const char *get_winbind_netbios_name(void) +{ + struct winbindd_response response; + + static fstring winbind_netbios_name; + + if (*winbind_netbios_name) { + return winbind_netbios_name; + } + + ZERO_STRUCT(response); + + /* Send off request */ + + if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) != + NSS_STATUS_SUCCESS) { + d_printf("could not obtain winbind netbios name!\n"); + return NULL; + } + + fstrcpy(winbind_netbios_name, response.data.netbios_name); + + return winbind_netbios_name; + +} + /* Authenticate a user with a plaintext password */ static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout_diagnostics) @@ -146,11 +176,116 @@ static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout return (result == NSS_STATUS_SUCCESS); } -static void manage_squid_basic_request(enum squid_mode squid_mode) +static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + /* Send off request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + fstrcpy(request.data.auth_crap.user, ntlmssp_state->user); + + fstrcpy(request.data.auth_crap.domain, ntlmssp_state->domain); + fstrcpy(request.data.auth_crap.workstation, ntlmssp_state->workstation); + + memcpy(request.data.auth_crap.chal, ntlmssp_state->chal.data, + MIN(ntlmssp_state->chal.length, 8)); + + memcpy(request.data.auth_crap.lm_resp, ntlmssp_state->lm_resp.data, + MIN(ntlmssp_state->lm_resp.length, sizeof(request.data.auth_crap.lm_resp))); + + memcpy(request.data.auth_crap.nt_resp, ntlmssp_state->lm_resp.data, + MIN(ntlmssp_state->nt_resp.length, sizeof(request.data.auth_crap.nt_resp))); + + request.data.auth_crap.lm_resp_len = ntlmssp_state->lm_resp.length; + request.data.auth_crap.nt_resp_len = ntlmssp_state->nt_resp.length; + + result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response); + + /* Display response */ + + if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) { + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS(response.data.auth.nt_status); +} + +static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, + char *buf, int length) +{ + static NTLMSSP_STATE *ntlmssp_state; + DATA_BLOB request, reply; + NTSTATUS nt_status; + + if (!ntlmssp_state) { + ntlmssp_server_start(&ntlmssp_state); + ntlmssp_state->check_password = winbind_pw_check; + ntlmssp_state->get_domain = get_winbind_domain; + ntlmssp_state->get_global_myname = get_winbind_netbios_name; + } + + if (strlen(buf) < 3) { + x_fprintf(x_stdout, "BH\n"); + return; + } + + request = base64_decode_data_blob(buf + 3); + + DEBUG(0, ("got NTLMSSP packet:\n")); + dump_data(0, request.data, request.length); + + nt_status = ntlmssp_server_update(ntlmssp_state, request, &reply); + + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + char *reply_base64 = base64_encode_data_blob(reply); + x_fprintf(x_stdout, "TT %s\n", reply_base64); + SAFE_FREE(reply_base64); + data_blob_free(&reply); + } else if (!NT_STATUS_IS_OK(nt_status)) { + x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status)); + } else { + x_fprintf(x_stdout, "AF %s\\%s\n", ntlmssp_state->domain, ntlmssp_state->user); + } + + data_blob_free(&request); +} + +static void manage_squid_basic_request(enum squid_mode squid_mode, + char *buf, int length) +{ + char *user, *pass; + user=buf; + + pass=memchr(buf,' ',length); + if (!pass) { + DEBUG(2, ("Password not found. Denying access\n")); + x_fprintf(x_stderr, "ERR\n"); + return; + } + *pass='\0'; + pass++; + + if (squid_mode == SQUID_2_5_BASIC) { + rfc1738_unescape(user); + rfc1738_unescape(pass); + } + + if (check_plaintext_auth(user, pass, False)) { + x_fprintf(x_stdout, "OK\n"); + } else { + x_fprintf(x_stdout, "ERR\n"); + } +} + +static void manage_squid_request(enum squid_mode squid_mode) { char buf[SQUID_BUFFER_SIZE+1]; int length; - char *c, *user, *pass; + char *c; static BOOL err; if (x_fgets(buf, sizeof(buf)-1, x_stdin) == NULL) { @@ -181,37 +316,21 @@ static void manage_squid_basic_request(enum squid_mode squid_mode) x_fprintf(x_stderr, "ERR\n"); return; } - - user=buf; - - pass=memchr(buf,' ',length); - if (!pass) { - DEBUG(2, ("Password not found. Denying access\n")); - x_fprintf(x_stderr, "ERR\n"); - return; - } - *pass='\0'; - pass++; - if (squid_mode == SQUID_2_5_BASIC) { - rfc1738_unescape(user); - rfc1738_unescape(pass); - } - - if (check_plaintext_auth(user, pass, False)) { - x_fprintf(x_stdout, "OK\n"); - } else { - x_fprintf(x_stdout, "ERR\n"); + if (squid_mode == SQUID_2_5_BASIC || squid_mode == SQUID_2_4_BASIC) { + manage_squid_basic_request(squid_mode, buf, length); + } else if (squid_mode == SQUID_2_5_NTLMSSP) { + manage_squid_ntlmssp_request(squid_mode, buf, length); } } -static void squid_basic(enum squid_mode squid_mode) { +static void squid_stream(enum squid_mode squid_mode) { /* initialize FDescs */ x_setbuf(x_stdout, NULL); x_setbuf(x_stderr, NULL); while(1) { - manage_squid_basic_request(squid_mode); + manage_squid_request(squid_mode); } } @@ -392,10 +511,12 @@ int main(int argc, const char **argv) } if (helper_protocol) { - if (strcmp(helper_protocol, "squid-2.5-basic")== 0) { - squid_basic(SQUID_2_5_BASIC); + if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) { + squid_stream(SQUID_2_5_NTLMSSP); + } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) { + squid_stream(SQUID_2_5_BASIC); } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) { - squid_basic(SQUID_2_4_BASIC); + squid_stream(SQUID_2_4_BASIC); } else { fprintf(stderr, "unknown helper protocol [%s]\n", helper_protocol); exit(1); diff --git a/source/web/cgi.c b/source/web/cgi.c index 018dd3602fd..35f32662833 100644 --- a/source/web/cgi.c +++ b/source/web/cgi.c @@ -291,37 +291,6 @@ static void cgi_web_auth(void) passwd_free(&pwd); } -/*************************************************************************** -decode a base64 string in-place - simple and slow algorithm - ***************************************************************************/ -static void base64_decode(char *s) -{ - const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - int bit_offset, byte_offset, idx, i, n; - unsigned char *d = (unsigned char *)s; - char *p; - - n=i=0; - - while (*s && (p=strchr_m(b64,*s))) { - idx = (int)(p - b64); - byte_offset = (i*6)/8; - bit_offset = (i*6)%8; - d[byte_offset] &= ~((1<<(8-bit_offset))-1); - if (bit_offset < 3) { - d[byte_offset] |= (idx << (2-bit_offset)); - n = byte_offset+1; - } else { - d[byte_offset] |= (idx >> (bit_offset-2)); - d[byte_offset+1] = 0; - d[byte_offset+1] |= (idx << (8-(bit_offset-2))) & 0xFF; - n = byte_offset+2; - } - s++; i++; - } - /* null terminate */ - d[n] = 0; -} /*************************************************************************** handle a http authentication line