mirror of
https://github.com/samba-team/samba.git
synced 2025-03-24 10:50:22 +03:00
Merge from HEAD:
- NTLMSSP over SPENGO (sesssion-setup-and-x) cleanup and code refactor. - also consequential changes to the NTLMSSP and SPNEGO parsing functions - and the client code that uses the same functions - Add ntlm_auth, a NTLMSSP authentication interface for use by applications like Squid and Apache. - also consquential changes to use common code for base64 encode/decode. - Winbind changes to support ntlm_auth (I don't want this program to need to read smb.conf, instead getting all it's details over the pipe). - nmbd changes for fstrcat() instead of fstrcpy(). Andrew Bartlett (This used to be commit fbb46da79cf322570a7e3318100c304bbf33409e)
This commit is contained in:
parent
2326525950
commit
1cba0a7579
@ -111,7 +111,7 @@ BIN_PROGS1 = bin/smbclient@EXEEXT@ bin/net@EXEEXT@ bin/smbspool@EXEEXT@ \
|
||||
BIN_PROGS2 = bin/smbcontrol@EXEEXT@ bin/smbtree@EXEEXT@ bin/tdbbackup@EXEEXT@ \
|
||||
bin/nmblookup@EXEEXT@ bin/pdbedit@EXEEXT@
|
||||
BIN_PROGS3 = bin/smbpasswd@EXEEXT@ bin/rpcclient@EXEEXT@ bin/smbcacls@EXEEXT@ \
|
||||
bin/profiles@EXEEXT@ bin/smbgroupedit@EXEEXT@
|
||||
bin/profiles@EXEEXT@ bin/smbgroupedit@EXEEXT@ bin/ntlm_auth@EXEEXT@ \
|
||||
|
||||
TORTURE_PROGS = bin/smbtorture@EXEEXT@ bin/msgtest@EXEEXT@ \
|
||||
bin/masktest@EXEEXT@ bin/locktest@EXEEXT@ \
|
||||
@ -186,7 +186,8 @@ LIBSMB_OBJ = libsmb/clientgen.o libsmb/cliconnect.o libsmb/clifile.o \
|
||||
libsmb/smberr.o libsmb/credentials.o libsmb/pwd_cache.o \
|
||||
libsmb/clioplock.o libsmb/errormap.o libsmb/clirap2.o \
|
||||
libsmb/passchange.o libsmb/unexpected.o libsmb/doserr.o \
|
||||
libsmb/namecache.o $(RPC_PARSE_OBJ1)
|
||||
libsmb/namecache.o libsmb/ntlmssp.o \
|
||||
$(RPC_PARSE_OBJ1)
|
||||
|
||||
LIBMSRPC_OBJ = rpc_client/cli_lsarpc.o rpc_client/cli_samr.o \
|
||||
rpc_client/cli_netlogon.o rpc_client/cli_srvsvc.o \
|
||||
@ -256,7 +257,7 @@ UNIGRP_OBJ = libsmb/netlogon_unigrp.o
|
||||
|
||||
AUTH_OBJ = auth/auth.o auth/auth_sam.o auth/auth_server.o auth/auth_domain.o \
|
||||
auth/auth_rhosts.o auth/auth_unix.o auth/auth_util.o auth/auth_winbind.o \
|
||||
auth/auth_builtin.o auth/auth_compat.o \
|
||||
auth/auth_builtin.o auth/auth_compat.o auth/auth_ntlmssp.o \
|
||||
$(PLAINTEXT_AUTH_OBJ) $(UNIGRP_OBJ)
|
||||
|
||||
MANGLE_OBJ = smbd/mangle.o smbd/mangle_hash.o smbd/mangle_map.o smbd/mangle_hash2.o
|
||||
@ -539,6 +540,8 @@ POPT_OBJS=popt/findme.o popt/popt.o popt/poptconfig.o \
|
||||
|
||||
TDBBACKUP_OBJ = tdb/tdbbackup.o $(TDBBASE_OBJ)
|
||||
|
||||
NTLM_AUTH_OBJ = utils/ntlm_auth.o $(LIBSMB_OBJ) $(POPT_LIB_OBJ)
|
||||
|
||||
######################################################################
|
||||
# now the rules...
|
||||
######################################################################
|
||||
@ -869,6 +872,12 @@ bin/wbinfo@EXEEXT@: $(WBINFO_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \
|
||||
@$(LINK) -o $@ $(WBINFO_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \
|
||||
$(UBIQX_OBJ) $(SECRETS_OBJ) $(LIBS) @BUILD_POPT@
|
||||
|
||||
bin/ntlm_auth@EXEEXT@: $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \
|
||||
$(UBIQX_OBJ) @BUILD_POPT@ bin/.dummy
|
||||
@echo Linking $@
|
||||
@$(LINK) -o $@ $(NTLM_AUTH_OBJ) $(PARAM_OBJ) $(LIB_OBJ) \
|
||||
$(UBIQX_OBJ) $(LIBS) @BUILD_POPT@
|
||||
|
||||
bin/pam_smbpass.@SHLIBEXT@: $(PAM_SMBPASS_PICOOBJ)
|
||||
@echo "Linking shared library $@"
|
||||
$(SHLD) $(LDSHFLAGS) -o $@ $(PAM_SMBPASS_PICOOBJ) -lpam $(DYNEXP) $(LIBS) -lc
|
||||
|
@ -57,7 +57,8 @@ static const uint8 *get_ntlm_challenge(struct auth_context *auth_context)
|
||||
TALLOC_CTX *mem_ctx;
|
||||
|
||||
if (auth_context->challenge.length) {
|
||||
DEBUG(5, ("get_ntlm_challenge (auth subsystem): returning previous challenge (normal)\n"));
|
||||
DEBUG(5, ("get_ntlm_challenge (auth subsystem): returning previous challenge by module %s (normal)\n",
|
||||
auth_context->challenge_set_by));
|
||||
return auth_context->challenge.data;
|
||||
}
|
||||
|
||||
@ -190,6 +191,12 @@ static NTSTATUS check_ntlm_password(const struct auth_context *auth_context,
|
||||
|
||||
DEBUG(3, ("check_ntlm_password: mapped user is: [%s]\\[%s]@[%s]\n",
|
||||
user_info->domain.str, user_info->internal_username.str, user_info->wksta_name.str));
|
||||
|
||||
if (auth_context->challenge.length != 8) {
|
||||
DEBUG(0, ("check_ntlm_password: Invalid challenge stored for this auth context - cannot continue\n"));
|
||||
return NT_STATUS_LOGON_FAILURE;
|
||||
}
|
||||
|
||||
if (auth_context->challenge_set_by)
|
||||
DEBUG(10, ("check_ntlm_password: auth_context challenge created by %s\n",
|
||||
auth_context->challenge_set_by));
|
||||
@ -441,6 +448,7 @@ NTSTATUS make_auth_context_fixed(struct auth_context **auth_context, uchar chal[
|
||||
}
|
||||
|
||||
(*auth_context)->challenge = data_blob(chal, 8);
|
||||
(*auth_context)->challenge_set_by = "fixed";
|
||||
return nt_status;
|
||||
}
|
||||
|
||||
|
@ -55,4 +55,8 @@ typedef struct {
|
||||
#define OID_KERBEROS5_OLD "1 2 840 48018 1 2 2"
|
||||
#define OID_KERBEROS5 "1 2 840 113554 1 2 2"
|
||||
|
||||
#define SPNGEO_NEG_RESULT_ACCEPT 0
|
||||
#define SPNGEO_NEG_RESULT_INCOMPLETE 1
|
||||
#define SPNGEO_NEG_RESULT_REJECT 2
|
||||
|
||||
#endif /* _ASN_1_H */
|
||||
|
@ -149,4 +149,13 @@ struct auth_init_function_entry {
|
||||
|
||||
auth_init_function init;
|
||||
};
|
||||
|
||||
typedef struct auth_ntlmssp_state
|
||||
{
|
||||
TALLOC_CTX *mem_ctx;
|
||||
struct auth_context *auth_context;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
struct ntlmssp_state *ntlmssp_state;
|
||||
} AUTH_NTLMSSP_STATE;
|
||||
|
||||
#endif /* _SMBAUTH_H_ */
|
||||
|
@ -71,9 +71,20 @@ typedef struct ntlmssp_state
|
||||
{
|
||||
TALLOC_CTX *mem_ctx;
|
||||
enum NTLMSSP_ROLE role;
|
||||
struct auth_context *auth_context;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
BOOL unicode;
|
||||
char *orig_user;
|
||||
char *orig_domain;
|
||||
char *user;
|
||||
char *domain;
|
||||
char *workstation;
|
||||
DATA_BLOB lm_resp;
|
||||
DATA_BLOB nt_resp;
|
||||
DATA_BLOB chal;
|
||||
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;
|
||||
|
||||
|
@ -1535,6 +1535,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;
|
||||
size_t 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)
|
||||
{
|
||||
|
@ -465,7 +465,8 @@ static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, const char *user,
|
||||
|
||||
neg_flags = NTLMSSP_NEGOTIATE_UNICODE |
|
||||
NTLMSSP_NEGOTIATE_128 |
|
||||
NTLMSSP_NEGOTIATE_NTLM;
|
||||
NTLMSSP_NEGOTIATE_NTLM |
|
||||
NTLMSSP_REQUEST_TARGET;
|
||||
|
||||
memset(sess_key, 0, 16);
|
||||
|
||||
@ -476,8 +477,8 @@ static BOOL cli_session_setup_ntlmssp(struct cli_state *cli, const char *user,
|
||||
"NTLMSSP",
|
||||
NTLMSSP_NEGOTIATE,
|
||||
neg_flags,
|
||||
workgroup, strlen(workgroup),
|
||||
cli->calling.name, strlen(cli->calling.name) + 1);
|
||||
workgroup,
|
||||
cli->calling.name);
|
||||
DEBUG(10, ("neg_flags: %0X, workgroup: %s, calling name %s\n",
|
||||
neg_flags, workgroup, cli->calling.name));
|
||||
/* and wrap it in a SPNEGO wrapper */
|
||||
|
@ -386,51 +386,6 @@ BOOL spnego_parse_challenge(DATA_BLOB blob,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
generate a spnego NTLMSSP challenge packet given two security blobs
|
||||
The second challenge is optional
|
||||
*/
|
||||
BOOL spnego_gen_challenge(DATA_BLOB *blob,
|
||||
DATA_BLOB *chal1, DATA_BLOB *chal2)
|
||||
{
|
||||
ASN1_DATA data;
|
||||
|
||||
ZERO_STRUCT(data);
|
||||
|
||||
asn1_push_tag(&data,ASN1_CONTEXT(1));
|
||||
asn1_push_tag(&data,ASN1_SEQUENCE(0));
|
||||
|
||||
asn1_push_tag(&data,ASN1_CONTEXT(0));
|
||||
asn1_write_enumerated(&data,1);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_push_tag(&data,ASN1_CONTEXT(1));
|
||||
asn1_write_OID(&data, OID_NTLMSSP);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_push_tag(&data,ASN1_CONTEXT(2));
|
||||
asn1_write_OctetString(&data, chal1->data, chal1->length);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
/* the second challenge is optional (XP doesn't send it) */
|
||||
if (chal2) {
|
||||
asn1_push_tag(&data,ASN1_CONTEXT(3));
|
||||
asn1_write_OctetString(&data, chal2->data, chal2->length);
|
||||
asn1_pop_tag(&data);
|
||||
}
|
||||
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
if (data.has_error) {
|
||||
return False;
|
||||
}
|
||||
|
||||
*blob = data_blob(data.data, data.length);
|
||||
asn1_free(&data);
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
generate a SPNEGO NTLMSSP auth packet. This will contain the encrypted passwords
|
||||
*/
|
||||
@ -485,18 +440,37 @@ BOOL spnego_parse_auth(DATA_BLOB blob, DATA_BLOB *auth)
|
||||
/*
|
||||
generate a minimal SPNEGO NTLMSSP response packet. Doesn't contain much.
|
||||
*/
|
||||
DATA_BLOB spnego_gen_auth_response(void)
|
||||
DATA_BLOB spnego_gen_auth_response(DATA_BLOB *ntlmssp_reply, NTSTATUS nt_status)
|
||||
{
|
||||
ASN1_DATA data;
|
||||
DATA_BLOB ret;
|
||||
uint8 negResult;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
negResult = SPNGEO_NEG_RESULT_ACCEPT;
|
||||
} else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
negResult = SPNGEO_NEG_RESULT_INCOMPLETE;
|
||||
} else {
|
||||
negResult = SPNGEO_NEG_RESULT_REJECT;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(data);
|
||||
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(1));
|
||||
asn1_push_tag(&data, ASN1_SEQUENCE(0));
|
||||
asn1_push_tag(&data, ASN1_CONTEXT(0));
|
||||
asn1_write_enumerated(&data, 0);
|
||||
asn1_write_enumerated(&data, negResult);
|
||||
asn1_pop_tag(&data);
|
||||
if (negResult == SPNGEO_NEG_RESULT_INCOMPLETE) {
|
||||
asn1_push_tag(&data,ASN1_CONTEXT(1));
|
||||
asn1_write_OID(&data, OID_NTLMSSP);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
asn1_push_tag(&data,ASN1_CONTEXT(2));
|
||||
asn1_write_OctetString(&data, ntlmssp_reply->data, ntlmssp_reply->length);
|
||||
asn1_pop_tag(&data);
|
||||
}
|
||||
|
||||
asn1_pop_tag(&data);
|
||||
asn1_pop_tag(&data);
|
||||
|
||||
@ -514,8 +488,9 @@ DATA_BLOB spnego_gen_auth_response(void)
|
||||
format specifiers are:
|
||||
|
||||
U = unicode string (input is unix string)
|
||||
a = address (1 byte type, 1 byte length, unicode string, all inline)
|
||||
A = ASCII string (pointer + length) Actually same as B
|
||||
a = address (input is BOOL unicode, char *unix_string)
|
||||
(1 byte type, 1 byte length, unicode/ASCII string, all inline)
|
||||
A = ASCII string (input is unix string)
|
||||
B = data blob (pointer + length)
|
||||
b = data blob in header (pointer + length)
|
||||
D
|
||||
@ -531,6 +506,7 @@ BOOL msrpc_gen(DATA_BLOB *blob,
|
||||
uint8 *b;
|
||||
int head_size=0, data_size=0;
|
||||
int head_ofs, data_ofs;
|
||||
BOOL unicode;
|
||||
|
||||
/* first scan the format to work out the header and body size */
|
||||
va_start(ap, format);
|
||||
@ -541,12 +517,21 @@ BOOL msrpc_gen(DATA_BLOB *blob,
|
||||
head_size += 8;
|
||||
data_size += str_charnum(s) * 2;
|
||||
break;
|
||||
case 'A':
|
||||
s = va_arg(ap, char *);
|
||||
head_size += 8;
|
||||
data_size += str_ascii_charnum(s);
|
||||
break;
|
||||
case 'a':
|
||||
unicode = va_arg(ap, BOOL);
|
||||
n = va_arg(ap, int);
|
||||
s = va_arg(ap, char *);
|
||||
data_size += (str_charnum(s) * 2) + 4;
|
||||
if (unicode) {
|
||||
data_size += (str_charnum(s) * 2) + 4;
|
||||
} else {
|
||||
data_size += (str_ascii_charnum(s)) + 4;
|
||||
}
|
||||
break;
|
||||
case 'A':
|
||||
case 'B':
|
||||
b = va_arg(ap, uint8 *);
|
||||
head_size += 8;
|
||||
@ -586,20 +571,39 @@ BOOL msrpc_gen(DATA_BLOB *blob,
|
||||
push_string(NULL, blob->data+data_ofs, s, n*2, STR_UNICODE|STR_NOALIGN);
|
||||
data_ofs += n*2;
|
||||
break;
|
||||
case 'A':
|
||||
s = va_arg(ap, char *);
|
||||
n = str_ascii_charnum(s);
|
||||
SSVAL(blob->data, head_ofs, n); head_ofs += 2;
|
||||
SSVAL(blob->data, head_ofs, n); head_ofs += 2;
|
||||
SIVAL(blob->data, head_ofs, data_ofs); head_ofs += 4;
|
||||
push_string(NULL, blob->data+data_ofs, s, n, STR_ASCII|STR_NOALIGN);
|
||||
data_ofs += n;
|
||||
break;
|
||||
case 'a':
|
||||
unicode = va_arg(ap, BOOL);
|
||||
n = va_arg(ap, int);
|
||||
SSVAL(blob->data, data_ofs, n); data_ofs += 2;
|
||||
s = va_arg(ap, char *);
|
||||
n = str_charnum(s);
|
||||
SSVAL(blob->data, data_ofs, n*2); data_ofs += 2;
|
||||
if (0 < n) {
|
||||
push_string(NULL, blob->data+data_ofs, s, n*2,
|
||||
STR_UNICODE|STR_NOALIGN);
|
||||
if (unicode) {
|
||||
n = str_charnum(s);
|
||||
SSVAL(blob->data, data_ofs, n*2); data_ofs += 2;
|
||||
if (0 < n) {
|
||||
push_string(NULL, blob->data+data_ofs, s, n*2,
|
||||
STR_UNICODE|STR_NOALIGN);
|
||||
}
|
||||
data_ofs += n*2;
|
||||
} else {
|
||||
n = str_ascii_charnum(s);
|
||||
SSVAL(blob->data, data_ofs, n); data_ofs += 2;
|
||||
if (0 < n) {
|
||||
push_string(NULL, blob->data+data_ofs, s, n,
|
||||
STR_ASCII|STR_NOALIGN);
|
||||
}
|
||||
data_ofs += n;
|
||||
}
|
||||
data_ofs += n*2;
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
case 'B':
|
||||
b = va_arg(ap, uint8 *);
|
||||
n = va_arg(ap, int);
|
||||
@ -714,7 +718,7 @@ BOOL msrpc_parse(DATA_BLOB *blob,
|
||||
break;
|
||||
case 'C':
|
||||
s = va_arg(ap, char *);
|
||||
head_ofs += pull_string(NULL, p, blob->data+head_ofs, -1,
|
||||
head_ofs += pull_string(NULL, p, blob->data+head_ofs, sizeof(p),
|
||||
blob->length - head_ofs,
|
||||
STR_ASCII|STR_TERMINATE);
|
||||
if (strcmp(s, p) != 0) {
|
||||
|
@ -272,8 +272,8 @@ reporting %s domain %s 0x%x ntversion=%x lm_nt token=%x lm_20 token=%x\n",
|
||||
pull_ucs2_fstring(asccomp, unicomp);
|
||||
DEBUG(3,("process_logon_packet: SAMLOGON user %s\n", ascuser));
|
||||
|
||||
fstrcpy(reply_name,"\\\\"); /* Here it wants \\LOGONSERVER. */
|
||||
fstrcpy(reply_name+2,my_name);
|
||||
fstrcpy(reply_name, "\\\\"); /* Here it wants \\LOGONSERVER. */
|
||||
fstrcat(reply_name, my_name);
|
||||
|
||||
DEBUG(3,("process_logon_packet: SAMLOGON request from %s(%s) for %s, returning logon svr %s domain %s code %x token=%x\n",
|
||||
asccomp,inet_ntoa(p->ip), ascuser, reply_name, lp_workgroup(),
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -23,7 +23,8 @@
|
||||
#include "includes.h"
|
||||
|
||||
uint32 global_client_caps = 0;
|
||||
static struct auth_context *ntlmssp_auth_context = NULL;
|
||||
|
||||
static struct auth_ntlmssp_state *global_ntlmssp_state;
|
||||
|
||||
/*
|
||||
on a logon error possibly map the error to success if "map to guest"
|
||||
@ -67,6 +68,37 @@ static void add_signature(char *outbuf)
|
||||
set_message_end(outbuf,p);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
send a security blob via a session setup reply
|
||||
****************************************************************************/
|
||||
static BOOL reply_sesssetup_blob(connection_struct *conn, char *outbuf,
|
||||
DATA_BLOB blob, NTSTATUS nt_status)
|
||||
{
|
||||
char *p;
|
||||
|
||||
set_message(outbuf,4,0,True);
|
||||
|
||||
/* we set NT_STATUS_MORE_PROCESSING_REQUIRED to tell the other end
|
||||
that we aren't finished yet */
|
||||
|
||||
nt_status = nt_status_squash(nt_status);
|
||||
SIVAL(outbuf, smb_rcls, NT_STATUS_V(nt_status));
|
||||
SSVAL(outbuf, smb_vwv0, 0xFF); /* no chaining possible */
|
||||
SSVAL(outbuf, smb_vwv3, blob.length);
|
||||
p = smb_buf(outbuf);
|
||||
|
||||
/* should we cap this? */
|
||||
memcpy(p, blob.data, blob.length);
|
||||
p += blob.length;
|
||||
|
||||
p += srvstr_push(outbuf, p, "Unix", -1, STR_TERMINATE);
|
||||
p += srvstr_push(outbuf, p, "Samba", -1, STR_TERMINATE);
|
||||
p += srvstr_push(outbuf, p, lp_workgroup(), -1, STR_TERMINATE);
|
||||
set_message_end(outbuf,p);
|
||||
|
||||
return send_smb(smbd_server_fd(),outbuf);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
Do a 'guest' logon, getting back the
|
||||
****************************************************************************/
|
||||
@ -209,30 +241,54 @@ static int reply_spnego_kerberos(connection_struct *conn,
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
send a security blob via a session setup reply
|
||||
****************************************************************************/
|
||||
static BOOL reply_sesssetup_blob(connection_struct *conn, char *outbuf,
|
||||
DATA_BLOB blob, uint32 errcode)
|
||||
send a session setup reply, wrapped in SPNEGO.
|
||||
get vuid and check first.
|
||||
end the NTLMSSP exchange context if we are OK/complete fail
|
||||
***************************************************************************/
|
||||
static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *outbuf,
|
||||
AUTH_NTLMSSP_STATE **auth_ntlmssp_state,
|
||||
DATA_BLOB *ntlmssp_blob, NTSTATUS nt_status)
|
||||
{
|
||||
char *p;
|
||||
BOOL ret;
|
||||
DATA_BLOB response;
|
||||
struct auth_serversupplied_info *server_info;
|
||||
server_info = (*auth_ntlmssp_state)->server_info;
|
||||
|
||||
set_message(outbuf,4,0,True);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
nt_status = do_map_to_guest(nt_status,
|
||||
&server_info,
|
||||
(*auth_ntlmssp_state)->ntlmssp_state->user,
|
||||
(*auth_ntlmssp_state)->ntlmssp_state->domain);
|
||||
}
|
||||
|
||||
/* we set NT_STATUS_MORE_PROCESSING_REQUIRED to tell the other end
|
||||
that we aren't finished yet */
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
int sess_vuid;
|
||||
sess_vuid = register_vuid(server_info, (*auth_ntlmssp_state)->ntlmssp_state->user /* check this for weird */);
|
||||
|
||||
if (sess_vuid == -1) {
|
||||
nt_status = NT_STATUS_LOGON_FAILURE;
|
||||
} else {
|
||||
|
||||
set_message(outbuf,4,0,True);
|
||||
SSVAL(outbuf, smb_vwv3, 0);
|
||||
|
||||
if ((*auth_ntlmssp_state)->server_info->guest) {
|
||||
SSVAL(outbuf,smb_vwv2,1);
|
||||
}
|
||||
|
||||
SSVAL(outbuf,smb_uid,sess_vuid);
|
||||
}
|
||||
}
|
||||
|
||||
SIVAL(outbuf, smb_rcls, errcode);
|
||||
SSVAL(outbuf, smb_vwv0, 0xFF); /* no chaining possible */
|
||||
SSVAL(outbuf, smb_vwv3, blob.length);
|
||||
p = smb_buf(outbuf);
|
||||
memcpy(p, blob.data, blob.length);
|
||||
p += blob.length;
|
||||
p += srvstr_push(outbuf, p, "Unix", -1, STR_TERMINATE);
|
||||
p += srvstr_push(outbuf, p, "Samba", -1, STR_TERMINATE);
|
||||
p += srvstr_push(outbuf, p, lp_workgroup(), -1, STR_TERMINATE);
|
||||
set_message_end(outbuf,p);
|
||||
|
||||
return send_smb(smbd_server_fd(),outbuf);
|
||||
response = spnego_gen_auth_response(ntlmssp_blob, nt_status);
|
||||
ret = reply_sesssetup_blob(conn, outbuf, response, nt_status);
|
||||
data_blob_free(&response);
|
||||
|
||||
if (!ret || !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
|
||||
auth_ntlmssp_end(&global_ntlmssp_state);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -247,12 +303,9 @@ static int reply_spnego_negotiate(connection_struct *conn,
|
||||
char *OIDs[ASN1_MAX_OIDS];
|
||||
DATA_BLOB secblob;
|
||||
int i;
|
||||
uint32 ntlmssp_command, neg_flags, chal_flags;
|
||||
DATA_BLOB chal, spnego_chal;
|
||||
const uint8 *cryptkey;
|
||||
DATA_BLOB chal;
|
||||
BOOL got_kerberos = False;
|
||||
NTSTATUS nt_status;
|
||||
char *cliname=NULL, *domname=NULL;
|
||||
|
||||
/* parse out the OIDs and the first sec blob */
|
||||
if (!parse_negTokenTarg(blob1, OIDs, &secblob)) {
|
||||
@ -278,95 +331,26 @@ static int reply_spnego_negotiate(connection_struct *conn,
|
||||
}
|
||||
#endif
|
||||
|
||||
/* parse the NTLMSSP packet */
|
||||
#if 0
|
||||
file_save("secblob.dat", secblob.data, secblob.length);
|
||||
#endif
|
||||
|
||||
if (!msrpc_parse(&secblob, "CddAA",
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command,
|
||||
&neg_flags,
|
||||
&cliname,
|
||||
&domname)) {
|
||||
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
|
||||
}
|
||||
|
||||
data_blob_free(&secblob);
|
||||
|
||||
if (ntlmssp_command != NTLMSSP_NEGOTIATE) {
|
||||
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
|
||||
if (global_ntlmssp_state) {
|
||||
auth_ntlmssp_end(&global_ntlmssp_state);
|
||||
}
|
||||
|
||||
debug_ntlmssp_flags(neg_flags);
|
||||
|
||||
if (ntlmssp_auth_context) {
|
||||
(ntlmssp_auth_context->free)(&ntlmssp_auth_context);
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&ntlmssp_auth_context))) {
|
||||
nt_status = auth_ntlmssp_start(&global_ntlmssp_state);
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return ERROR_NT(nt_status);
|
||||
}
|
||||
|
||||
cryptkey = ntlmssp_auth_context->get_ntlm_challenge(ntlmssp_auth_context);
|
||||
nt_status = auth_ntlmssp_update(global_ntlmssp_state,
|
||||
secblob, &chal);
|
||||
|
||||
/* Give them the challenge. For now, ignore neg_flags and just
|
||||
return the flags we want. Obviously this is not correct */
|
||||
|
||||
chal_flags = NTLMSSP_NEGOTIATE_UNICODE |
|
||||
NTLMSSP_NEGOTIATE_128 |
|
||||
NTLMSSP_NEGOTIATE_NTLM |
|
||||
NTLMSSP_CHAL_TARGET_INFO;
|
||||
|
||||
{
|
||||
DATA_BLOB domain_blob, struct_blob;
|
||||
fstring dnsname, dnsdomname;
|
||||
data_blob_free(&secblob);
|
||||
|
||||
reply_spnego_ntlmssp(conn, outbuf, &global_ntlmssp_state,
|
||||
&chal, nt_status);
|
||||
|
||||
msrpc_gen(&domain_blob,
|
||||
"U",
|
||||
lp_workgroup());
|
||||
|
||||
fstrcpy(dnsdomname, (SEC_ADS == lp_security())?lp_realm():"");
|
||||
strlower(dnsdomname);
|
||||
|
||||
fstrcpy(dnsname, global_myname());
|
||||
fstrcat(dnsname, ".");
|
||||
fstrcat(dnsname, dnsdomname);
|
||||
strlower(dnsname);
|
||||
|
||||
msrpc_gen(&struct_blob, "aaaaa",
|
||||
2, lp_workgroup(),
|
||||
1, global_myname(),
|
||||
4, dnsdomname,
|
||||
3, dnsname,
|
||||
0, "");
|
||||
|
||||
msrpc_gen(&chal, "CdUdbddB",
|
||||
"NTLMSSP",
|
||||
NTLMSSP_CHALLENGE,
|
||||
lp_workgroup(),
|
||||
chal_flags,
|
||||
cryptkey, 8,
|
||||
0, 0,
|
||||
struct_blob.data, struct_blob.length);
|
||||
|
||||
data_blob_free(&domain_blob);
|
||||
data_blob_free(&struct_blob);
|
||||
}
|
||||
|
||||
if (!spnego_gen_challenge(&spnego_chal, &chal, &chal)) {
|
||||
DEBUG(3,("Failed to generate challenge\n"));
|
||||
data_blob_free(&chal);
|
||||
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
|
||||
}
|
||||
|
||||
/* now tell the client to send the auth packet */
|
||||
reply_sesssetup_blob(conn, outbuf, spnego_chal, NT_STATUS_V(NT_STATUS_MORE_PROCESSING_REQUIRED));
|
||||
|
||||
data_blob_free(&chal);
|
||||
data_blob_free(&spnego_chal);
|
||||
|
||||
/* and tell smbd that we have already replied to this packet */
|
||||
/* already replied */
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -378,23 +362,8 @@ static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf,
|
||||
int length, int bufsize,
|
||||
DATA_BLOB blob1)
|
||||
{
|
||||
DATA_BLOB auth, response;
|
||||
char *workgroup = NULL, *user = NULL, *machine = NULL;
|
||||
DATA_BLOB lmhash, nthash, sess_key;
|
||||
DATA_BLOB plaintext_password = data_blob(NULL, 0);
|
||||
uint32 ntlmssp_command, neg_flags;
|
||||
DATA_BLOB auth, auth_reply;
|
||||
NTSTATUS nt_status;
|
||||
int sess_vuid;
|
||||
BOOL as_guest;
|
||||
uint32 auth_flags = AUTH_FLAG_NONE;
|
||||
auth_usersupplied_info *user_info = NULL;
|
||||
auth_serversupplied_info *server_info = NULL;
|
||||
|
||||
/* we must have setup the auth context by now */
|
||||
if (!ntlmssp_auth_context) {
|
||||
DEBUG(2,("ntlmssp_auth_context is NULL in reply_spnego_auth\n"));
|
||||
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
|
||||
}
|
||||
|
||||
if (!spnego_parse_auth(blob1, &auth)) {
|
||||
#if 0
|
||||
@ -403,157 +372,32 @@ static int reply_spnego_auth(connection_struct *conn, char *inbuf, char *outbuf,
|
||||
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
|
||||
}
|
||||
|
||||
/* now the NTLMSSP encoded auth hashes */
|
||||
if (!msrpc_parse(&auth, "CdBBUUUBd",
|
||||
"NTLMSSP",
|
||||
&ntlmssp_command,
|
||||
&lmhash,
|
||||
&nthash,
|
||||
&workgroup,
|
||||
&user,
|
||||
&machine,
|
||||
&sess_key,
|
||||
&neg_flags)) {
|
||||
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
|
||||
}
|
||||
nt_status = auth_ntlmssp_update(global_ntlmssp_state,
|
||||
auth, &auth_reply);
|
||||
|
||||
data_blob_free(&auth);
|
||||
data_blob_free(&sess_key);
|
||||
|
||||
DEBUG(3,("Got user=[%s] workgroup=[%s] machine=[%s] len1=%d len2=%d\n",
|
||||
user, workgroup, machine, lmhash.length, nthash.length));
|
||||
|
||||
/* the client has given us its machine name (which we otherwise would not get on port 445).
|
||||
we need to possibly reload smb.conf if smb.conf includes depend on the machine name */
|
||||
|
||||
set_remote_machine_name(machine);
|
||||
|
||||
/* setup the string used by %U */
|
||||
sub_set_smb_name(user);
|
||||
|
||||
reload_services(True);
|
||||
|
||||
#if 0
|
||||
file_save("nthash1.dat", nthash.data, nthash.length);
|
||||
file_save("lmhash1.dat", lmhash.data, lmhash.length);
|
||||
#endif
|
||||
|
||||
if (lmhash.length) {
|
||||
auth_flags |= AUTH_FLAG_LM_RESP;
|
||||
}
|
||||
|
||||
if (nthash.length == 24) {
|
||||
auth_flags |= AUTH_FLAG_NTLM_RESP;
|
||||
} else if (nthash.length > 24) {
|
||||
auth_flags |= AUTH_FLAG_NTLMv2_RESP;
|
||||
};
|
||||
|
||||
nt_status = make_user_info_map(&user_info, user, workgroup, machine,
|
||||
lmhash, nthash, plaintext_password,
|
||||
auth_flags, True);
|
||||
|
||||
/* it looks a bit weird, but this function returns int type... */
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return ERROR_NT(NT_STATUS_NO_MEMORY);
|
||||
}
|
||||
|
||||
nt_status = ntlmssp_auth_context->check_ntlm_password(ntlmssp_auth_context, user_info, &server_info);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
nt_status = do_map_to_guest(nt_status, &server_info, user, workgroup);
|
||||
}
|
||||
|
||||
SAFE_FREE(workgroup);
|
||||
SAFE_FREE(machine);
|
||||
|
||||
(ntlmssp_auth_context->free)(&ntlmssp_auth_context);
|
||||
|
||||
free_user_info(&user_info);
|
||||
|
||||
data_blob_free(&lmhash);
|
||||
|
||||
data_blob_free(&nthash);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
SAFE_FREE(user);
|
||||
return ERROR_NT(nt_status_squash(nt_status));
|
||||
}
|
||||
|
||||
as_guest = server_info->guest;
|
||||
|
||||
sess_vuid = register_vuid(server_info, user);
|
||||
free_server_info(&server_info);
|
||||
|
||||
SAFE_FREE(user);
|
||||
|
||||
if (sess_vuid == -1) {
|
||||
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
|
||||
}
|
||||
|
||||
set_message(outbuf,4,0,True);
|
||||
SSVAL(outbuf, smb_vwv3, 0);
|
||||
|
||||
if (as_guest) {
|
||||
SSVAL(outbuf,smb_vwv2,1);
|
||||
}
|
||||
|
||||
add_signature(outbuf);
|
||||
|
||||
SSVAL(outbuf,smb_uid,sess_vuid);
|
||||
SSVAL(inbuf,smb_uid,sess_vuid);
|
||||
|
||||
response = spnego_gen_auth_response();
|
||||
reply_sesssetup_blob(conn, outbuf, response, 0);
|
||||
reply_spnego_ntlmssp(conn, outbuf, &global_ntlmssp_state,
|
||||
&auth_reply, nt_status);
|
||||
|
||||
data_blob_free(&auth_reply);
|
||||
|
||||
/* and tell smbd that we have already replied to this packet */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
reply to a session setup spnego anonymous packet
|
||||
****************************************************************************/
|
||||
static int reply_spnego_anonymous(connection_struct *conn, char *inbuf, char *outbuf,
|
||||
int length, int bufsize)
|
||||
{
|
||||
int sess_vuid;
|
||||
auth_serversupplied_info *server_info = NULL;
|
||||
NTSTATUS nt_status;
|
||||
|
||||
nt_status = check_guest_password(&server_info);
|
||||
|
||||
if (!NT_STATUS_IS_OK(nt_status)) {
|
||||
return ERROR_NT(nt_status_squash(nt_status));
|
||||
}
|
||||
|
||||
sess_vuid = register_vuid(server_info, lp_guestaccount());
|
||||
|
||||
free_server_info(&server_info);
|
||||
|
||||
if (sess_vuid == -1) {
|
||||
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
|
||||
}
|
||||
|
||||
set_message(outbuf,4,0,True);
|
||||
SSVAL(outbuf, smb_vwv3, 0);
|
||||
add_signature(outbuf);
|
||||
|
||||
SSVAL(outbuf,smb_uid,sess_vuid);
|
||||
SSVAL(inbuf,smb_uid,sess_vuid);
|
||||
|
||||
return chain_reply(inbuf,outbuf,length,bufsize);
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
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;
|
||||
DATA_BLOB blob1;
|
||||
int ret;
|
||||
size_t bufrem;
|
||||
|
||||
DEBUG(3,("Doing spnego session setup\n"));
|
||||
|
||||
@ -564,12 +408,13 @@ static int reply_sesssetup_and_X_spnego(connection_struct *conn, char *inbuf,cha
|
||||
p = (uint8 *)smb_buf(inbuf);
|
||||
|
||||
if (SVAL(inbuf, smb_vwv7) == 0) {
|
||||
/* an anonymous request */
|
||||
return reply_spnego_anonymous(conn, inbuf, outbuf, length, bufsize);
|
||||
/* an invalid request */
|
||||
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
|
||||
}
|
||||
|
||||
bufrem = smb_bufrem(inbuf, p);
|
||||
/* pull the spnego blob */
|
||||
blob1 = data_blob(p, SVAL(inbuf, smb_vwv7));
|
||||
blob1 = data_blob(p, MIN(bufrem, SVAL(inbuf, smb_vwv7)));
|
||||
|
||||
#if 0
|
||||
file_save("negotiate.dat", blob1.data, blob1.length);
|
||||
@ -786,6 +631,10 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,
|
||||
nt_status = check_guest_password(&server_info);
|
||||
|
||||
} else if (doencrypt) {
|
||||
if (!negprot_global_auth_context) {
|
||||
DEBUG(0, ("reply_sesssetup_and_X: Attempted encrypted session setup without negprot denied!\n"));
|
||||
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
|
||||
}
|
||||
nt_status = make_user_info_for_reply_enc(&user_info, user, domain,
|
||||
lm_resp, nt_resp);
|
||||
if (NT_STATUS_IS_OK(nt_status)) {
|
||||
@ -830,10 +679,8 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf,
|
||||
}
|
||||
|
||||
/* it's ok - setup a reply */
|
||||
if (Protocol < PROTOCOL_NT1) {
|
||||
set_message(outbuf,3,0,True);
|
||||
} else {
|
||||
set_message(outbuf,3,0,True);
|
||||
set_message(outbuf,3,0,True);
|
||||
if (Protocol >= PROTOCOL_NT1) {
|
||||
add_signature(outbuf);
|
||||
/* perhaps grab OS version here?? */
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user