diff options
-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; |