#include "includes.h"
-#if HAVE_KRB5
+uint32 global_client_caps = 0;
+static struct auth_context *ntlmssp_auth_context = NULL;
+
+/*
+ on a logon error possibly map the error to success if "map to guest"
+ is set approriately
+*/
+static NTSTATUS do_map_to_guest(NTSTATUS status, auth_serversupplied_info **server_info,
+ const char *user, const char *domain)
+{
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_SUCH_USER)) {
+ if ((lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_USER) ||
+ (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD)) {
+ DEBUG(3,("No such user %s [%s] - using guest account\n",
+ user, domain));
+ make_server_info_guest(server_info);
+ status = NT_STATUS_OK;
+ }
+ }
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_WRONG_PASSWORD)) {
+ if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD) {
+ DEBUG(3,("Registered username %s for guest access\n",user));
+ make_server_info_guest(server_info);
+ status = NT_STATUS_OK;
+ }
+ }
+
+ return status;
+}
+
+
+/****************************************************************************
+ Add the standard 'Samba' signature to the end of the session setup.
+****************************************************************************/
+static void add_signature(char *outbuf)
+{
+ char *p;
+ p = smb_buf(outbuf);
+ 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);
+}
+
+/****************************************************************************
+ Do a 'guest' logon, getting back the
+****************************************************************************/
+static NTSTATUS check_guest_password(auth_serversupplied_info **server_info)
+{
+ struct auth_context *auth_context;
+ auth_usersupplied_info *user_info = NULL;
+
+ NTSTATUS nt_status;
+ unsigned char chal[8];
+
+ ZERO_STRUCT(chal);
+
+ DEBUG(3,("Got anonymous request\n"));
+
+ if (!NT_STATUS_IS_OK(nt_status = make_auth_context_fixed(&auth_context, chal))) {
+ return nt_status;
+ }
+
+ if (!make_user_info_guest(&user_info)) {
+ (auth_context->free)(&auth_context);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = auth_context->check_ntlm_password(auth_context, user_info, server_info);
+ (auth_context->free)(&auth_context);
+ free_user_info(&user_info);
+ return nt_status;
+}
+
+
+#ifdef HAVE_KRB5
/****************************************************************************
reply to a session setup spnego negotiate packet for kerberos
****************************************************************************/
DATA_BLOB *secblob)
{
DATA_BLOB ticket;
- krb5_context context;
- krb5_auth_context auth_context = NULL;
- krb5_keytab keytab = NULL;
- krb5_data packet;
- krb5_ticket *tkt = NULL;
- int ret;
- char *realm, *client, *p;
+ char *client, *p;
const struct passwd *pw;
char *user;
int sess_vuid;
+ NTSTATUS ret;
+ DATA_BLOB auth_data;
auth_serversupplied_info *server_info = NULL;
-
- realm = lp_realm();
+ ADS_STRUCT *ads;
if (!spnego_parse_krb5_wrap(*secblob, &ticket)) {
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
}
- ret = krb5_init_context(&context);
- if (ret) {
- DEBUG(1,("krb5_init_context failed (%s)\n", error_message(ret)));
- return ERROR_NT(NT_STATUS_LOGON_FAILURE);
- }
-
- packet.length = ticket.length;
- packet.data = (krb5_pointer)ticket.data;
-
-#if 0
- file_save("/tmp/ticket.dat", ticket.data, ticket.length);
-#endif
-
- if ((ret = krb5_rd_req(context, &auth_context, &packet,
- NULL, keytab, NULL, &tkt))) {
- DEBUG(3,("krb5_rd_req failed (%s)\n",
- error_message(ret)));
- return ERROR_NT(NT_STATUS_LOGON_FAILURE);
- }
+ ads = ads_init(NULL, NULL, NULL, NULL);
- if ((ret = krb5_unparse_name(context, tkt->enc_part2->client,
- &client))) {
- DEBUG(3,("krb5_unparse_name failed (%s)\n",
- error_message(ret)));
+ ret = ads_verify_ticket(ads, &ticket, &client, &auth_data);
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(1,("Failed to verify incoming ticket!\n"));
+ ads_destroy(&ads);
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
}
p = strchr_m(client, '@');
if (!p) {
DEBUG(3,("Doesn't look like a valid principal\n"));
+ ads_destroy(&ads);
return ERROR_NT(NT_STATUS_LOGON_FAILURE);
}
*p = 0;
- if (strcasecmp(p+1, realm) != 0) {
- DEBUG(3,("Ticket for incorrect realm %s\n", p+1));
- return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ if (strcasecmp(p+1, ads->realm) != 0) {
+ DEBUG(3,("Ticket for foreign realm %s@%s\n", client, p+1));
+ if (!lp_allow_trusted_domains()) {
+ return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ }
+ /* this gives a fully qualified user name (ie. with full realm).
+ that leads to very long usernames, but what else can we do? */
+ asprintf(&user, "%s%s%s", p+1, lp_winbind_separator(), client);
+ } else {
+ user = strdup(client);
}
-
- user = client;
+ ads_destroy(&ads);
/* the password is good - let them in */
pw = smb_getpwnam(user,False);
+ if (!pw && !strstr(user, lp_winbind_separator())) {
+ char *user2;
+ /* try it with a winbind domain prefix */
+ asprintf(&user2, "%s%s%s", lp_workgroup(), lp_winbind_separator(), user);
+ pw = smb_getpwnam(user2,False);
+ if (pw) {
+ free(user);
+ user = user2;
+ }
+ }
+
if (!pw) {
DEBUG(1,("Username %s is invalid on this system\n",user));
- return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ return ERROR_NT(NT_STATUS_NO_SUCH_USER);
}
if (!make_server_info_pw(&server_info,pw)) {
return ERROR_NT(NT_STATUS_NO_MEMORY);
}
- sess_vuid = register_vuid(server_info, user, False);
+ sess_vuid = register_vuid(server_info, user);
+ free(user);
free_server_info(&server_info);
if (sess_vuid == -1) {
set_message(outbuf,4,0,True);
SSVAL(outbuf, smb_vwv3, 0);
- p = smb_buf(outbuf);
- 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);
+ add_signature(outbuf);
SSVAL(outbuf,smb_uid,sess_vuid);
SSVAL(inbuf,smb_uid,sess_vuid);
int i;
uint32 ntlmssp_command, neg_flags;
DATA_BLOB sess_key, chal, spnego_chal;
- uint8 cryptkey[8];
+ const uint8 *cryptkey;
BOOL got_kerberos = False;
+ NTSTATUS nt_status;
/* parse out the OIDs and the first sec blob */
if (!parse_negTokenTarg(blob1, OIDs, &secblob)) {
}
DEBUG(3,("Got secblob of size %d\n", secblob.length));
-#if HAVE_KRB5
+#ifdef HAVE_KRB5
if (got_kerberos) {
int ret = reply_spnego_kerberos(conn, inbuf, outbuf,
length, bufsize, &secblob);
DEBUG(3,("Got neg_flags=%08x\n", neg_flags));
- if (!last_challenge(cryptkey)) {
- return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ 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))) {
+ return ERROR_NT(nt_status);
}
+ cryptkey = ntlmssp_auth_context->get_ntlm_challenge(ntlmssp_auth_context);
+
/* Give them the challenge. For now, ignore neg_flags and just
return the flags we want. Obviously this is not correct */
DATA_BLOB blob1)
{
DATA_BLOB auth;
- char *workgroup, *user, *machine;
+ char *workgroup = NULL, *user = NULL, *machine = NULL;
DATA_BLOB lmhash, nthash, sess_key;
DATA_BLOB plaintext_password = data_blob(NULL, 0);
- DATA_BLOB sec_blob;
uint32 ntlmssp_command, neg_flags;
NTSTATUS nt_status;
int sess_vuid;
- char *p;
- char chal[8];
+ BOOL as_guest;
auth_usersupplied_info *user_info = NULL;
auth_serversupplied_info *server_info = NULL;
file_save("lmhash1.dat", lmhash.data, lmhash.length);
#endif
- if (!last_challenge(chal)) {
- DEBUG(0,("Encrypted login but no challange set!\n"));
- return ERROR_NT(NT_STATUS_LOGON_FAILURE);
- }
- sec_blob = data_blob(chal, 8);
- if (!sec_blob.data) {
- return ERROR_NT(NT_STATUS_NO_MEMORY);
- }
-
if (!make_user_info_map(&user_info,
user, workgroup,
- machine, sec_blob,
+ machine,
lmhash, nthash,
plaintext_password,
neg_flags, True)) {
return ERROR_NT(NT_STATUS_NO_MEMORY);
}
-
- nt_status = check_password(user_info, &server_info);
-
+
+ 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));
}
- sess_vuid = register_vuid(server_info, user, False);
+ 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);
- p = smb_buf(outbuf);
- 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);
+
+ if (as_guest) {
+ SSVAL(outbuf,smb_vwv2,1);
+ }
+
+ add_signature(outbuf);
SSVAL(outbuf,smb_uid,sess_vuid);
SSVAL(inbuf,smb_uid,sess_vuid);
int length, int bufsize)
{
int sess_vuid;
- char *p;
auth_serversupplied_info *server_info = NULL;
+ NTSTATUS nt_status;
- DEBUG(3,("Got anonymous request\n"));
+ 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());
- make_server_info_guest(&server_info);
- sess_vuid = register_vuid(server_info, lp_guestaccount(-1), False);
free_server_info(&server_info);
if (sess_vuid == -1) {
set_message(outbuf,4,0,True);
SSVAL(outbuf, smb_vwv3, 0);
- p = smb_buf(outbuf);
- 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);
+ add_signature(outbuf);
SSVAL(outbuf,smb_uid,sess_vuid);
SSVAL(inbuf,smb_uid,sess_vuid);
{
uint8 *p;
DATA_BLOB blob1;
- extern uint32 global_client_caps;
int ret;
DEBUG(3,("Doing spnego session setup\n"));
DATA_BLOB nt_resp;
DATA_BLOB plaintext_password;
pstring user;
+ pstring sub_user; /* Sainitised username for substituion */
fstring domain;
fstring native_os;
fstring native_lanman;
- BOOL guest=False;
static BOOL done_sesssetup = False;
extern BOOL global_encrypted_passwords_negotiated;
extern BOOL global_spnego_negotiated;
- extern uint32 global_client_caps;
extern int Protocol;
extern fstring remote_machine;
extern userdom_struct current_user_info;
extern int max_send;
auth_usersupplied_info *user_info = NULL;
+ extern struct auth_context *negprot_global_auth_context;
auth_serversupplied_info *server_info = NULL;
+ NTSTATUS nt_status;
+
BOOL doencrypt = global_encrypted_passwords_negotiated;
START_PROFILE(SMBsesssetupX);
ZERO_STRUCT(plaintext_password);
DEBUG(3,("wct=%d flg2=0x%x\n", CVAL(inbuf, smb_wct), SVAL(inbuf, smb_flg2)));
-
+
/* a SPNEGO session setup has 12 command words, whereas a normal
NT1 session setup has 13. See the cifs spec. */
if (CVAL(inbuf, smb_wct) == 12 &&
(SVAL(inbuf, smb_flg2) & FLAGS2_EXTENDED_SECURITY)) {
- return reply_sesssetup_and_X_spnego(conn, inbuf, outbuf, length, bufsize);
- }
+ if (!global_spnego_negotiated) {
+ DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at SPNEGO session setup when it was not negoitiated.\n"));
+ return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
- if (global_spnego_negotiated) {
- DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at 'normal' session setup after negotiating spnego.\n"));
- return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ return reply_sesssetup_and_X_spnego(conn, inbuf, outbuf, length, bufsize);
}
smb_bufsize = SVAL(inbuf,smb_vwv2);
lm_resp = data_blob(smb_buf(inbuf), passlen1);
} else {
plaintext_password = data_blob(smb_buf(inbuf), passlen1+1);
- if (!plaintext_password.data) {
- DEBUG(0,("reply_sesssetup_and_X: malloc failed for plaintext_password!\n"));
- return ERROR_NT(NT_STATUS_NO_MEMORY);
- } else {
- /* Ensure null termination */
- plaintext_password.data[passlen1] = 0;
- }
+ /* Ensure null termination */
+ plaintext_password.data[passlen1] = 0;
}
srvstr_pull(inbuf, user, smb_buf(inbuf)+passlen1, sizeof(user), -1, STR_TERMINATE);
+ *domain = 0;
} else {
uint16 passlen1 = SVAL(inbuf,smb_vwv7);
passlen2 = 0;
}
- if (lp_restrict_anonymous()) {
- /* there seems to be no reason behind the
- * differences in MS clients formatting
- * various info like the domain, NativeOS, and
- * NativeLanMan fields. Win95 in particular
- * seems to have an extra null byte between
- * the username and the domain, or the
- * password length calculation is wrong, which
- * throws off the string extraction routines
- * below. This makes the value of domain be
- * the empty string, which fails the restrict
- * anonymous check further down. This
- * compensates for that, and allows browsing
- * to work in mixed NT and win95 environments
- * even when restrict anonymous is true. AAB
- * */
- dump_data(100, p, 0x70);
- DEBUG(9, ("passlen1=%d, passlen2=%d\n", passlen1, passlen2));
- if (ra_type == RA_WIN95 && !passlen1 && !passlen2 && p[0] == 0 && p[1] == 0) {
- DEBUG(0, ("restrict anonymous parameter used in a win95 environment!\n"));
- DEBUG(0, ("client is win95 and broken passlen1 offset -- attempting fix\n"));
- DEBUG(0, ("if win95 cilents are having difficulty browsing, you will be unable to use restrict anonymous\n"));
- passlen1 = 1;
- }
- }
-
/* Save the lanman2 password and the NT md4 password. */
if ((doencrypt) && (passlen1 != 0) && (passlen1 != 24)) {
DEBUG(3,("sesssetupX:name=[%s]\\[%s]@[%s]\n", domain, user, remote_machine));
- /* If no username is sent use the guest account */
- if (!*user) {
- pstrcpy(user,lp_guestaccount(-1));
- guest = True;
+ if (*user) {
+ if (global_spnego_negotiated) {
+
+ /* This has to be here, becouse this is a perfectly valid behaviour for guest logons :-( */
+
+ DEBUG(0,("reply_sesssetup_and_X: Rejecting attempt at 'normal' session setup after negotiating spnego.\n"));
+ return ERROR_NT(NT_STATUS_UNSUCCESSFUL);
+ }
+ pstrcpy(sub_user, user);
+ } else {
+ pstrcpy(sub_user, lp_guestaccount());
}
- pstrcpy(current_user_info.smb_name,user);
+ pstrcpy(current_user_info.smb_name,sub_user);
reload_services(True);
data_blob_free(&lm_resp);
data_blob_free(&nt_resp);
- data_blob_free(&plaintext_password);
+ data_blob_clear_free(&plaintext_password);
- guest = True;
- map_username(user);
- add_session_user(user);
+ map_username(sub_user);
+ add_session_user(sub_user);
+ /* Then force it to null for the benfit of the code below */
+ *user = 0;
}
- if (done_sesssetup && lp_restrict_anonymous()) {
- /* tests show that even if browsing is done over
- * already validated connections without a username
- * and password the domain is still provided, which it
- * wouldn't be if it was a purely anonymous
- * connection. So, in order to restrict anonymous, we
- * only deny connections that have no session
- * information. If a domain has been provided, then
- * it's not a purely anonymous connection. AAB */
- if (!*user && !*domain) {
- DEBUG(0, ("restrict anonymous is True and anonymous connection attempted. Denying access.\n"));
-
- data_blob_free(&lm_resp);
- data_blob_free(&nt_resp);
- data_blob_free(&plaintext_password);
+ if (!*user) {
- END_PROFILE(SMBsesssetupX);
- return ERROR_DOS(ERRDOS,ERRnoaccess);
- }
- }
-
- if (!guest) {
- NTSTATUS nt_status;
- if (!make_user_info_for_reply(&user_info,
- user, domain,
- lm_resp, nt_resp,
- plaintext_password, doencrypt)) {
- return ERROR_NT(NT_STATUS_NO_MEMORY);
+ nt_status = check_guest_password(&server_info);
+
+ } else if (doencrypt) {
+ if (!make_user_info_for_reply_enc(&user_info,
+ user, domain,
+ lm_resp, nt_resp)) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ } else {
+ nt_status = negprot_global_auth_context->check_ntlm_password(negprot_global_auth_context,
+ user_info,
+ &server_info);
}
+ } else {
+ struct auth_context *plaintext_auth_context = NULL;
+ const uint8 *chal;
+ if (NT_STATUS_IS_OK(nt_status = make_auth_context_subsystem(&plaintext_auth_context))) {
+ chal = plaintext_auth_context->get_ntlm_challenge(plaintext_auth_context);
+
+ if (!make_user_info_for_reply(&user_info,
+ user, domain, chal,
+ plaintext_password)) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ }
- nt_status = check_password(user_info, &server_info);
-
- free_user_info(&user_info);
-
- data_blob_free(&lm_resp);
- data_blob_free(&nt_resp);
- data_blob_free(&plaintext_password);
-
- if (!NT_STATUS_IS_OK(nt_status)) {
- if NT_STATUS_EQUAL(nt_status, NT_STATUS_NO_SUCH_USER) {
- if ((lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_USER) ||
- (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD)) {
- DEBUG(3,("No such user %s [%s] - using guest account\n",user, domain));
- pstrcpy(user,lp_guestaccount(-1));
- guest = True;
-
- }
- } else if NT_STATUS_EQUAL(nt_status, NT_STATUS_WRONG_PASSWORD) {
- if (lp_map_to_guest() == MAP_TO_GUEST_ON_BAD_PASSWORD) {
- pstrcpy(user,lp_guestaccount(-1));
- DEBUG(3,("Registered username %s for guest access\n",user));
- guest = True;
- }
- /* Match WinXP and don't give the game away */
- return ERROR_NT(NT_STATUS_LOGON_FAILURE);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ nt_status = plaintext_auth_context->check_ntlm_password(plaintext_auth_context,
+ user_info,
+ &server_info);
+
+ (plaintext_auth_context->free)(&plaintext_auth_context);
}
-
- if (!guest) {
- free_server_info(&server_info);
- return ERROR_NT(nt_status_squash(nt_status));
- }
}
}
+
+ free_user_info(&user_info);
+
+ data_blob_free(&lm_resp);
+ data_blob_free(&nt_resp);
+ data_blob_clear_free(&plaintext_password);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ nt_status = do_map_to_guest(nt_status, &server_info, user, domain);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return ERROR_NT(nt_status_squash(nt_status));
+ }
/* it's ok - setup a reply */
if (Protocol < PROTOCOL_NT1) {
set_message(outbuf,3,0,True);
} else {
- char *p;
set_message(outbuf,3,0,True);
- p = smb_buf(outbuf);
- 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);
+ add_signature(outbuf);
/* perhaps grab OS version here?? */
}
- if (guest) {
+ if (server_info->guest) {
SSVAL(outbuf,smb_vwv2,1);
- free_server_info(&server_info);
- make_server_info_guest(&server_info);
} else {
const char *home_dir = pdb_get_homedir(server_info->sam_account);
const char *username = pdb_get_username(server_info->sam_account);
/* register the name and uid as being validated, so further connections
to a uid can get through without a password, on the same VC */
- sess_vuid = register_vuid(server_info, user, guest);
+ sess_vuid = register_vuid(server_info, sub_user);
free_server_info(&server_info);