diff options
author | Jeremy Allison <jra@samba.org> | 2007-08-21 01:43:22 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 12:30:08 -0500 |
commit | 57e2718e097bba57c96632064956f6ce048d8f28 (patch) | |
tree | 87afa5d9f5602334409b989cf122529a58fc0b20 /source3 | |
parent | b0b080799c542d1314870dcbfebd8e84d8002392 (diff) | |
download | samba-57e2718e097bba57c96632064956f6ce048d8f28.tar.gz samba-57e2718e097bba57c96632064956f6ce048d8f28.tar.bz2 samba-57e2718e097bba57c96632064956f6ce048d8f28.zip |
r24589: Refactor our vuid code so that we keep the same
vuid that was allocated whilst the connection is
being constructed and after the connection has been set up.
This is what Windows does and at least one client
(and HP printer) depends on this behaviour. As it
depends on the req struct not yet ported to SAMBA_3_2_0
(Volker, hint hint.... :-) I am not yet adding this
to that branch, but will investigate that tomorrow.
Jeremy.
(This used to be commit a54f2805df92c67e74a6764568eedebe394fd500)
Diffstat (limited to 'source3')
-rw-r--r-- | source3/lib/substitute.c | 2 | ||||
-rw-r--r-- | source3/smbd/password.c | 297 | ||||
-rw-r--r-- | source3/smbd/sesssetup.c | 122 |
3 files changed, 245 insertions, 176 deletions
diff --git a/source3/lib/substitute.c b/source3/lib/substitute.c index a20ea64143..64b343fabb 100644 --- a/source3/lib/substitute.c +++ b/source3/lib/substitute.c @@ -154,7 +154,7 @@ void set_current_user_info(const userdom_struct *pcui) { current_user_info = *pcui; /* The following is safe as current_user_info.smb_name - * has already been sanitised in register_vuid. */ + * has already been sanitised in register_existing_vuid. */ fstrcpy(smb_user_name, current_user_info.smb_name); } diff --git a/source3/smbd/password.c b/source3/smbd/password.c index 1a7dc33c61..847b8db082 100644 --- a/source3/smbd/password.c +++ b/source3/smbd/password.c @@ -30,13 +30,12 @@ static user_struct *validated_users; static int next_vuid = VUID_OFFSET; static int num_validated_vuids; -/**************************************************************************** - Check if a uid has been validated, and return an pointer to the user_struct - if it has. NULL if not. vuid is biased by an offset. This allows us to - tell random client vuid's (normally zero) from valid vuids. -****************************************************************************/ +enum server_allocated_state { SERVER_ALLOCATED_REQUIRED_YES, + SERVER_ALLOCATED_REQUIRED_NO, + SERVER_ALLOCATED_REQUIRED_ANY}; -user_struct *get_valid_user_struct(uint16 vuid) +static user_struct *get_valid_user_struct_internal(uint16 vuid, + enum server_allocated_state server_allocated) { user_struct *usp; int count=0; @@ -45,7 +44,20 @@ user_struct *get_valid_user_struct(uint16 vuid) return NULL; for (usp=validated_users;usp;usp=usp->next,count++) { - if (vuid == usp->vuid && usp->server_info) { + if (vuid == usp->vuid) { + switch (server_allocated) { + case SERVER_ALLOCATED_REQUIRED_YES: + if (usp->server_info == NULL) { + continue; + } + break; + case SERVER_ALLOCATED_REQUIRED_NO: + if (usp->server_info != NULL) { + continue; + } + case SERVER_ALLOCATED_REQUIRED_ANY: + break; + } if (count > 10) { DLIST_PROMOTE(validated_users, usp); } @@ -57,27 +69,33 @@ user_struct *get_valid_user_struct(uint16 vuid) } /**************************************************************************** - Get the user struct of a partial NTLMSSP login + Check if a uid has been validated, and return an pointer to the user_struct + if it has. NULL if not. vuid is biased by an offset. This allows us to + tell random client vuid's (normally zero) from valid vuids. ****************************************************************************/ -user_struct *get_partial_auth_user_struct(uint16 vuid) +user_struct *get_valid_user_struct(uint16 vuid) { - user_struct *usp; - int count=0; - - if (vuid == UID_FIELD_INVALID) - return NULL; + return get_valid_user_struct_internal(vuid, SERVER_ALLOCATED_REQUIRED_YES); +} - for (usp=validated_users;usp;usp=usp->next,count++) { - if (vuid == usp->vuid && !usp->server_info) { - if (count > 10) { - DLIST_PROMOTE(validated_users, usp); - } - return usp; - } +BOOL is_partial_auth_vuid(uint16 vuid) +{ + if (vuid == UID_FIELD_INVALID) { + return False; } + return get_valid_user_struct_internal(vuid, + SERVER_ALLOCATED_REQUIRED_NO) ? True : False; +} - return NULL; +/**************************************************************************** + Get the user struct of a partial NTLMSSP login +****************************************************************************/ + +user_struct *get_partial_auth_user_struct(uint16 vuid) +{ + return get_valid_user_struct_internal(vuid, + SERVER_ALLOCATED_REQUIRED_NO); } /**************************************************************************** @@ -86,11 +104,18 @@ user_struct *get_partial_auth_user_struct(uint16 vuid) void invalidate_vuid(uint16 vuid) { - user_struct *vuser = get_valid_user_struct(vuid); + user_struct *vuser = NULL; - if (vuser == NULL) + if (vuid == UID_FIELD_INVALID) { return; - + } + + vuser = get_valid_user_struct_internal(vuid, + SERVER_ALLOCATED_REQUIRED_ANY); + if (vuser == NULL) { + return; + } + session_yield(vuser); data_blob_free(&vuser->session_key); @@ -115,116 +140,127 @@ void invalidate_all_vuids(void) for (usp=validated_users;usp;usp=next) { next = usp->next; - invalidate_vuid(usp->vuid); } } -/** - * register that a valid login has been performed, establish 'session'. - * @param server_info The token returned from the authentication process. - * (now 'owned' by register_vuid) - * - * @param session_key The User session key for the login session (now also - * 'owned' by register_vuid) - * - * @param respose_blob The NT challenge-response, if available. (May be - * freed after this call) - * - * @param smb_name The untranslated name of the user - * - * @return Newly allocated vuid, biased by an offset. (This allows us to - * tell random client vuid's (normally zero) from valid vuids.) - * - */ +/**************************************************** + Create a new partial auth user struct. +*****************************************************/ -int register_vuid(auth_serversupplied_info *server_info, - DATA_BLOB session_key, DATA_BLOB response_blob, - const char *smb_name) +int register_initial_vuid(void) { user_struct *vuser; /* Paranoia check. */ if(lp_security() == SEC_SHARE) { - smb_panic("Tried to register uid in security=share"); + smb_panic("register_initial_vuid: " + "Tried to register uid in security=share"); } /* Limit allowed vuids to 16bits - VUID_OFFSET. */ if (num_validated_vuids >= 0xFFFF-VUID_OFFSET) { - data_blob_free(&session_key); return UID_FIELD_INVALID; } - if((vuser = TALLOC_ZERO_P(NULL, user_struct)) == NULL) { - DEBUG(0,("Failed to talloc users struct!\n")); - data_blob_free(&session_key); + if((vuser = talloc_zero(NULL, user_struct)) == NULL) { + DEBUG(0,("register_initial_vuid: " + "Failed to talloc users struct!\n")); return UID_FIELD_INVALID; } - /* Allocate a free vuid. Yes this is a linear search... :-) */ - while( get_valid_user_struct(next_vuid) != NULL ) { + /* Allocate a free vuid. Yes this is a linear search... */ + while( get_valid_user_struct_internal(next_vuid, + SERVER_ALLOCATED_REQUIRED_ANY) != NULL ) { next_vuid++; /* Check for vuid wrap. */ - if (next_vuid == UID_FIELD_INVALID) + if (next_vuid == UID_FIELD_INVALID) { next_vuid = VUID_OFFSET; + } } - DEBUG(10,("register_vuid: allocated vuid = %u\n", - (unsigned int)next_vuid )); + DEBUG(10,("register_initial_vuid: allocated vuid = %u\n", + (unsigned int)next_vuid )); vuser->vuid = next_vuid; - if (!server_info) { - /* - * This happens in an unfinished NTLMSSP session setup. We - * need to allocate a vuid between the first and second calls - * to NTLMSSP. - */ - next_vuid++; - num_validated_vuids++; - - vuser->server_info = NULL; - - DLIST_ADD(validated_users, vuser); - - return vuser->vuid; + /* + * This happens in an unfinished NTLMSSP session setup. We + * need to allocate a vuid between the first and second calls + * to NTLMSSP. + */ + next_vuid++; + num_validated_vuids++; + + DLIST_ADD(validated_users, vuser); + return vuser->vuid; +} + +/** + * register that a valid login has been performed, establish 'session'. + * @param server_info The token returned from the authentication process. + * (now 'owned' by register_existing_vuid) + * + * @param session_key The User session key for the login session (now also + * 'owned' by register_existing_vuid) + * + * @param respose_blob The NT challenge-response, if available. (May be + * freed after this call) + * + * @param smb_name The untranslated name of the user + * + * @return Newly allocated vuid, biased by an offset. (This allows us to + * tell random client vuid's (normally zero) from valid vuids.) + * + */ + +int register_existing_vuid(uint16 vuid, + auth_serversupplied_info *server_info, + DATA_BLOB session_key, + DATA_BLOB response_blob, + const char *smb_name) +{ + user_struct *vuser = get_partial_auth_user_struct(vuid); + if (!vuser) { + goto fail; } - /* use this to keep tabs on all our info from the authentication */ + /* Use this to keep tabs on all our info from the authentication */ vuser->server_info = server_info; - /* Ensure that the server_info will dissapear with the vuser it is now attached to */ + + /* Ensure that the server_info will disappear with + * the vuser it is now attached to */ + talloc_steal(vuser, vuser->server_info); /* the next functions should be done by a SID mapping system (SMS) as * the new real sam db won't have reference to unix uids or gids */ - + vuser->uid = server_info->uid; vuser->gid = server_info->gid; - + vuser->n_groups = server_info->n_groups; if (vuser->n_groups) { - if (!(vuser->groups = (gid_t *)talloc_memdup(vuser, server_info->groups, - sizeof(gid_t) * - vuser->n_groups))) { - DEBUG(0,("register_vuid: failed to talloc_memdup " - "vuser->groups\n")); - data_blob_free(&session_key); - TALLOC_FREE(vuser); - return UID_FIELD_INVALID; + if (!(vuser->groups = (gid_t *)talloc_memdup(vuser, + server_info->groups, + sizeof(gid_t)*vuser->n_groups))) { + DEBUG(0,("register_existing_vuid: " + "failed to talloc_memdup vuser->groups\n")); + goto fail; } } vuser->guest = server_info->guest; - fstrcpy(vuser->user.unix_name, server_info->unix_name); + fstrcpy(vuser->user.unix_name, server_info->unix_name); /* This is a potentially untrusted username */ alpha_strcpy(vuser->user.smb_name, smb_name, ". _-$", - sizeof(vuser->user.smb_name)); + sizeof(vuser->user.smb_name)); fstrcpy(vuser->user.domain, pdb_get_domain(server_info->sam_account)); fstrcpy(vuser->user.full_name, - pdb_get_fullname(server_info->sam_account)); + pdb_get_fullname(server_info->sam_account)); { /* Keep the homedir handy */ @@ -234,7 +270,7 @@ int register_vuid(auth_serversupplied_info *server_info, pdb_get_logon_script(server_info->sam_account); if (!IS_SAM_DEFAULT(server_info->sam_account, - PDB_UNIXHOMEDIR)) { + PDB_UNIXHOMEDIR)) { const char *unix_homedir = pdb_get_unix_homedir(server_info->sam_account); if (unix_homedir) { @@ -252,7 +288,7 @@ int register_vuid(auth_serversupplied_info *server_info, TALLOC_FREE(passwd); } } - + if (homedir) { vuser->homedir = homedir; } @@ -260,86 +296,83 @@ int register_vuid(auth_serversupplied_info *server_info, vuser->logon_script = logon_script; } } - vuser->session_key = session_key; - DEBUG(10,("register_vuid: (%u,%u) %s %s %s guest=%d\n", - (unsigned int)vuser->uid, - (unsigned int)vuser->gid, - vuser->user.unix_name, vuser->user.smb_name, - vuser->user.domain, vuser->guest )); + DEBUG(10,("register_existing_vuid: (%u,%u) %s %s %s guest=%d\n", + (unsigned int)vuser->uid, + (unsigned int)vuser->gid, + vuser->user.unix_name, vuser->user.smb_name, + vuser->user.domain, vuser->guest )); - DEBUG(3, ("User name: %s\tReal name: %s\n", vuser->user.unix_name, - vuser->user.full_name)); + DEBUG(3, ("register_existing_vuid: User name: %s\t" + "Real name: %s\n", vuser->user.unix_name, + vuser->user.full_name)); - if (server_info->ptok) { + if (server_info->ptok) { vuser->nt_user_token = dup_nt_token(vuser, server_info->ptok); } else { - DEBUG(1, ("server_info does not contain a user_token - " - "cannot continue\n")); - TALLOC_FREE(vuser); - data_blob_free(&session_key); - return UID_FIELD_INVALID; + DEBUG(1, ("register_existing_vuid: server_info does not " + "contain a user_token - cannot continue\n")); + goto fail; } - DEBUG(3,("UNIX uid %d is UNIX user %s, and will be vuid %u\n", - (int)vuser->uid,vuser->user.unix_name, vuser->vuid)); + DEBUG(3,("register_existing_vuid: UNIX uid %d is UNIX user %s, " + "and will be vuid %u\n", + (int)vuser->uid,vuser->user.unix_name, vuser->vuid)); next_vuid++; num_validated_vuids++; - DLIST_ADD(validated_users, vuser); - if (!session_claim(vuser)) { - DEBUG(1, ("Failed to claim session for vuid=%d\n", - vuser->vuid)); - invalidate_vuid(vuser->vuid); - return UID_FIELD_INVALID; + DEBUG(1, ("register_existing_vuid: Failed to claim session " + "for vuid=%d\n", + vuser->vuid)); + goto fail; } - /* Register a home dir service for this user iff - - (a) This is not a guest connection, - (b) we have a home directory defined - (c) there s not an existing static share by that name - - If a share exists by this name (autoloaded or not) reuse it . */ + /* Register a home dir service for this user if + (a) This is not a guest connection, + (b) we have a home directory defined + (c) there s not an existing static share by that name + If a share exists by this name (autoloaded or not) reuse it . */ vuser->homes_snum = -1; - - if ( (!vuser->guest) && vuser->unix_homedir && *(vuser->unix_homedir)) - { + if ( (!vuser->guest) && vuser->unix_homedir && *(vuser->unix_homedir)) { int servicenumber = lp_servicenumber(vuser->user.unix_name); - if ( servicenumber == -1 ) { DEBUG(3, ("Adding homes service for user '%s' using " - "home directory: '%s'\n", + "home directory: '%s'\n", vuser->user.unix_name, vuser->unix_homedir)); vuser->homes_snum = - add_home_service(vuser->user.unix_name, - vuser->user.unix_name, - vuser->unix_homedir); + add_home_service(vuser->user.unix_name, + vuser->user.unix_name, + vuser->unix_homedir); } else { DEBUG(3, ("Using static (or previously created) " - "service for user '%s'; path = '%s'\n", - vuser->user.unix_name, - lp_pathname(servicenumber) )); + "service for user '%s'; path = '%s'\n", + vuser->user.unix_name, + lp_pathname(servicenumber) )); vuser->homes_snum = servicenumber; } - } - + } + if (srv_is_signing_negotiated() && !vuser->guest && - !srv_signing_started()) { + !srv_signing_started()) { /* Try and turn on server signing on the first non-guest * sessionsetup. */ srv_set_signing(vuser->session_key, response_blob); } - + /* fill in the current_user_info struct */ set_current_user_info( &vuser->user ); + return vuser->vuid; + fail: - return vuser->vuid; + if (vuser) { + invalidate_vuid(vuid); + } + return UID_FIELD_INVALID; } /**************************************************************************** diff --git a/source3/smbd/sesssetup.c b/source3/smbd/sesssetup.c index 22ad43ff75..e63511c368 100644 --- a/source3/smbd/sesssetup.c +++ b/source3/smbd/sesssetup.c @@ -249,8 +249,8 @@ static void reply_spnego_kerberos(connection_struct *conn, fstring netbios_domain_name; struct passwd *pw; fstring user; - int sess_vuid; - NTSTATUS ret; + int sess_vuid = SVAL(req->inbuf, smb_uid); + NTSTATUS ret = NT_STATUS_OK; PAC_DATA *pac_data; DATA_BLOB ap_rep, ap_rep_wrapped, response; auth_serversupplied_info *server_info = NULL; @@ -535,18 +535,28 @@ static void reply_spnego_kerberos(connection_struct *conn, } } - /* register_vuid keeps the server info */ - /* register_vuid takes ownership of session_key, no need to free after this. - A better interface would copy it.... */ - sess_vuid = register_vuid(server_info, session_key, nullblob, client); + /* register_existing_vuid keeps the server info */ + /* register_existing_vuid takes ownership of session_key on success, + * no need to free after this on success. A better interface would copy + * it.... */ + + if (!is_partial_auth_vuid(sess_vuid)) { + sess_vuid = register_initial_vuid(); + } + sess_vuid = register_existing_vuid(sess_vuid, + server_info, + session_key, + nullblob, + client); SAFE_FREE(client); reply_outbuf(req, 4, 0); - SSVAL(req->outbuf,smb_uid,vuid); + SSVAL(req->outbuf,smb_uid,sess_vuid); if (sess_vuid == UID_FIELD_INVALID ) { ret = NT_STATUS_LOGON_FAILURE; + data_blob_free(&session_key); } else { /* current_user_info is changed on new vuid */ reload_services( True ); @@ -560,6 +570,8 @@ static void reply_spnego_kerberos(connection_struct *conn, SSVAL(req->outbuf, smb_uid, sess_vuid); sessionsetup_start_signing_engine(server_info, req->inbuf); + /* Successful logon. Keep this vuid. */ + *p_invalidate_vuid = False; } /* wrap that up in a nice GSS-API wrapping */ @@ -611,33 +623,43 @@ static void reply_spnego_ntlmssp(connection_struct *conn, SSVAL(req->outbuf, smb_uid, vuid); if (NT_STATUS_IS_OK(nt_status)) { - int sess_vuid; DATA_BLOB nullblob = data_blob_null; DATA_BLOB session_key = data_blob((*auth_ntlmssp_state)->ntlmssp_state->session_key.data, (*auth_ntlmssp_state)->ntlmssp_state->session_key.length); - /* register_vuid keeps the server info */ - sess_vuid = register_vuid(server_info, session_key, nullblob, (*auth_ntlmssp_state)->ntlmssp_state->user); + if (!is_partial_auth_vuid(vuid)) { + data_blob_free(&session_key); + nt_status = NT_STATUS_LOGON_FAILURE; + goto out; + } + /* register_existing_vuid keeps the server info */ + if (register_existing_vuid(vuid, + server_info, + session_key, nullblob, + (*auth_ntlmssp_state)->ntlmssp_state->user) != + vuid) { + data_blob_free(&session_key); + nt_status = NT_STATUS_LOGON_FAILURE; + goto out; + } + (*auth_ntlmssp_state)->server_info = NULL; - if (sess_vuid == UID_FIELD_INVALID ) { - nt_status = NT_STATUS_LOGON_FAILURE; - } else { - - /* current_user_info is changed on new vuid */ - reload_services( True ); + /* current_user_info is changed on new vuid */ + reload_services( True ); - SSVAL(req->outbuf, smb_vwv3, 0); - - if (server_info->guest) { - SSVAL(req->outbuf,smb_vwv2,1); - } - - SSVAL(req->outbuf,smb_uid,sess_vuid); + SSVAL(req->outbuf, smb_vwv3, 0); - sessionsetup_start_signing_engine(server_info, req->inbuf); + if (server_info->guest) { + SSVAL(req->outbuf,smb_vwv2,1); } + + SSVAL(req->outbuf,smb_uid,vuid); + + sessionsetup_start_signing_engine(server_info, req->inbuf); } + out: + if (wrap) { response = spnego_gen_auth_response(ntlmssp_blob, nt_status, OID_NTLMSSP); } else { @@ -655,8 +677,10 @@ static void reply_spnego_ntlmssp(connection_struct *conn, if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { /* NB. This is *NOT* an error case. JRA */ auth_ntlmssp_end(auth_ntlmssp_state); - /* Kill the intermediate vuid */ - invalidate_vuid(vuid); + if (!NT_STATUS_IS_OK(nt_status)) { + /* Kill the intermediate vuid */ + invalidate_vuid(vuid); + } } } @@ -1111,36 +1135,36 @@ static void reply_sesssetup_and_X_spnego(connection_struct *conn, } } - vuser = get_partial_auth_user_struct(vuid); - if (!vuser) { + /* Did we get a valid vuid ? */ + if (!is_partial_auth_vuid(vuid)) { + /* No, then try and see if this is an intermediate sessionsetup + * for a large SPNEGO packet. */ struct pending_auth_data *pad = get_pending_auth_data(smbpid); if (pad) { DEBUG(10,("reply_sesssetup_and_X_spnego: found pending vuid %u\n", (unsigned int)pad->vuid )); vuid = pad->vuid; - vuser = get_partial_auth_user_struct(vuid); } } - if (!vuser) { - vuid = register_vuid(NULL, data_blob_null, data_blob_null, NULL); - if (vuid == UID_FIELD_INVALID ) { + /* Do we have a valid vuid now ? */ + if (!is_partial_auth_vuid(vuid)) { + /* No, start a new authentication setup. */ + vuid = register_initial_vuid(); + if (vuid == UID_FIELD_INVALID) { data_blob_free(&blob1); reply_nterror(req, nt_status_squash( NT_STATUS_INVALID_PARAMETER)); return; } - - vuser = get_partial_auth_user_struct(vuid); } + vuser = get_partial_auth_user_struct(vuid); + /* This MUST be valid. */ if (!vuser) { - data_blob_free(&blob1); - reply_nterror(req, nt_status_squash( - NT_STATUS_INVALID_PARAMETER)); - return; + smb_panic("reply_sesssetup_and_X_spnego: invalid vuid."); } - + /* Large (greater than 4k) SPNEGO blobs are split into multiple * sessionsetup requests as the Windows limit on the security blob * field is 4k. Bug #4400. JRA. @@ -1617,13 +1641,25 @@ void reply_sesssetup_and_X(connection_struct *conn, struct smb_request *req) data_blob_free(&session_key); TALLOC_FREE(server_info); } else { - /* register_vuid keeps the server info */ - sess_vuid = register_vuid(server_info, session_key, - nt_resp.data ? nt_resp : lm_resp, - sub_user); + /* Ignore the initial vuid. */ + sess_vuid = register_initial_vuid(); if (sess_vuid == UID_FIELD_INVALID) { data_blob_free(&nt_resp); data_blob_free(&lm_resp); + data_blob_free(&session_key); + reply_nterror(req, nt_status_squash( + NT_STATUS_LOGON_FAILURE)); + } + /* register_existing_vuid keeps the server info */ + sess_vuid = register_existing_vuid(sess_vuid, + server_info, + session_key, + nt_resp.data ? nt_resp : lm_resp, + sub_user); + if (sess_vuid == UID_FIELD_INVALID) { + data_blob_free(&nt_resp); + data_blob_free(&lm_resp); + data_blob_free(&session_key); reply_nterror(req, nt_status_squash( NT_STATUS_LOGON_FAILURE)); return; |