From 20841a25ad2a45ec920d9ad41f1279cbe2aa196f Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Sun, 10 Apr 2005 07:39:51 +0000 Subject: r6270: Move the VUID handling to a IDR tree. This should avoid O(n) behaviour on session setups, and because we no longer need do deal with the linked list as much, the code is much simpiler too. We may be able to compleatly remove the tid and vuid linked lists, but I need to check. This patch also tries to clean up the VUID handling and session setups in general. To avoid security issues, we now have a distinction between VUIDs allocated for the session setup (to tie togeather the multiple round trips) and those used after authentication. Andrew Bartlett (This used to be commit 3e5775146d9ce6f0ac43aecae7e899b5324399ad) --- source4/smb_server/password.c | 122 ++++++++++++++++------------------ source4/smb_server/sesssetup.c | 143 +++++++++++++++++++++++++--------------- source4/smb_server/smb_server.c | 2 +- source4/smb_server/smb_server.h | 9 ++- 4 files changed, 157 insertions(+), 119 deletions(-) (limited to 'source4') diff --git a/source4/smb_server/password.c b/source4/smb_server/password.c index 3d7723936d..1132a8ed9a 100644 --- a/source4/smb_server/password.c +++ b/source4/smb_server/password.c @@ -2,6 +2,7 @@ Unix SMB/CIFS implementation. Password and authentication handling Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Andrew Bartlett 2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,39 +25,45 @@ /**************************************************************************** -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. +init the tcon structures ****************************************************************************/ -struct smbsrv_session *smbsrv_session_find(struct smbsrv_connection *smb_conn, uint16_t vuid) +void smbsrv_vuid_init(struct smbsrv_connection *smb_conn) { - struct smbsrv_session *sess; - int count=0; + smb_conn->sessions.idtree_vuid = idr_init(smb_conn); +} - if (vuid == UID_FIELD_INVALID) - return NULL; - for (sess=smb_conn->sessions.session_list; sess; sess=sess->next,count++) { - if (vuid == sess->vuid) { - if (count > 10) { - DLIST_PROMOTE(smb_conn->sessions.session_list, sess); - } - return sess; - } +/**************************************************************************** +Find the session structure assoicated with a VUID (not one from an in-progress session setup) +****************************************************************************/ +struct smbsrv_session *smbsrv_session_find(struct smbsrv_connection *smb_conn, uint16_t vuid) +{ + struct smbsrv_session *sess = idr_find(smb_conn->sessions.idtree_vuid, vuid); + if (sess && sess->finished_sesssetup) { + return sess; } - return NULL; } /**************************************************************************** -invalidate a uid + Find a VUID assoicated with an in-progress session setup ****************************************************************************/ -void smbsrv_invalidate_vuid(struct smbsrv_connection *smb_conn, uint16_t vuid) +struct smbsrv_session *smbsrv_session_find_sesssetup(struct smbsrv_connection *smb_conn, uint16_t vuid) { - struct smbsrv_session *sess = smbsrv_session_find(smb_conn, vuid); + struct smbsrv_session *sess = idr_find(smb_conn->sessions.idtree_vuid, vuid); + if (sess && !sess->finished_sesssetup) { + return sess; + } + return NULL; +} - if (sess == NULL) - return; +/**************************************************************************** +invalidate a session +****************************************************************************/ +static int smbsrv_session_destructor(void *p) +{ + struct smbsrv_session *sess = p; + struct smbsrv_connection *smb_conn = sess->smb_conn; DLIST_REMOVE(smb_conn->sessions.session_list, sess); @@ -65,20 +72,18 @@ void smbsrv_invalidate_vuid(struct smbsrv_connection *smb_conn, uint16_t vuid) /* REWRITE: conn_clear_vuid_cache(smb, vuid); */ smb_conn->sessions.num_validated_vuids--; + + idr_remove(smb_conn->sessions.idtree_vuid, sess->vuid); + return 0; } /**************************************************************************** -invalidate all vuid entries for this process +invalidate a uid ****************************************************************************/ -void smbsrv_invalidate_all_vuids(struct smbsrv_connection *smb_conn) +void smbsrv_invalidate_vuid(struct smbsrv_connection *smb_conn, uint16_t vuid) { - struct smbsrv_session *sess,*next=NULL; - - for (sess=smb_conn->sessions.session_list; sess; sess=next) { - next = sess->next; - - smbsrv_invalidate_vuid(smb_conn, sess->vuid); - } + struct smbsrv_session *sess = smbsrv_session_find(smb_conn, vuid); + talloc_free(sess); } /** @@ -93,56 +98,45 @@ void smbsrv_invalidate_all_vuids(struct smbsrv_connection *smb_conn) * */ -uint16_t smbsrv_register_session(struct smbsrv_connection *smb_conn, - struct auth_session_info *session_info, - struct gensec_security *gensec_ctx) +struct smbsrv_session *smbsrv_register_session(struct smbsrv_connection *smb_conn, + struct auth_session_info *session_info, + struct gensec_security *gensec_ctx) { struct smbsrv_session *sess = NULL; + int i; + + /* Ensure no vuid gets registered in share level security. */ + /* TODO: replace lp_security with a flag in smbsrv_connection */ + if (lp_security() == SEC_SHARE) + return UID_FIELD_INVALID; sess = talloc(smb_conn, struct smbsrv_session); - if(sess == NULL) { + if (sess == NULL) { DEBUG(0,("talloc(smb_conn->mem_ctx, struct smbsrv_session) failed\n")); - return UID_FIELD_INVALID; + return sess; } ZERO_STRUCTP(sess); - sess->vuid = UID_FIELD_INVALID; - /* Ensure no vuid gets registered in share level security. */ - /* TODO: replace lp_security with a flag in smbsrv_connection */ - if(lp_security() == SEC_SHARE) - return sess->vuid; - - /* Limit allowed vuids to 16bits - VUID_OFFSET. */ - if (smb_conn->sessions.num_validated_vuids >= 0xFFFF-VUID_OFFSET) - return sess->vuid; - - /* Allocate a free vuid. Yes this is a linear search... :-) */ - while (smbsrv_session_find(smb_conn, smb_conn->sessions.next_vuid) != NULL ) { - smb_conn->sessions.next_vuid++; - /* Check for vuid wrap. */ - if (smb_conn->sessions.next_vuid == UID_FIELD_INVALID) - smb_conn->sessions.next_vuid = VUID_OFFSET; + i = idr_get_new_above(smb_conn->sessions.idtree_vuid, sess, VUID_OFFSET, UINT16_MAX); + if (i == -1) { + DEBUG(1,("ERROR! Out of connection structures\n")); + talloc_free(sess); + return NULL; } + sess->vuid = i; - DEBUG(10,("register_vuid: allocated vuid = %u\n", - (uint_t)smb_conn->sessions.next_vuid)); - - sess->vuid = smb_conn->sessions.next_vuid; - smb_conn->sessions.next_vuid++; smb_conn->sessions.num_validated_vuids++; /* use this to keep tabs on all our info from the authentication */ - if (session_info) { - sess->session_info = talloc_reference(sess, session_info); - } - - if (gensec_ctx) { - sess->gensec_ctx = talloc_reference(sess, gensec_ctx); - } + sess->session_info = talloc_reference(sess, session_info); + + sess->gensec_ctx = talloc_reference(sess, gensec_ctx); sess->smb_conn = smb_conn; DLIST_ADD(smb_conn->sessions.session_list, sess); - return sess->vuid; + talloc_set_destructor(sess, smbsrv_session_destructor); + + return sess; } diff --git a/source4/smb_server/sesssetup.c b/source4/smb_server/sesssetup.c index 0f0dcb837e..4e87aa6de6 100644 --- a/source4/smb_server/sesssetup.c +++ b/source4/smb_server/sesssetup.c @@ -1,10 +1,10 @@ /* Unix SMB/CIFS implementation. handle SMBsessionsetup - Copyright (C) Andrew Tridgell 1998-2001 - Copyright (C) Andrew Bartlett 2001 - Copyright (C) Jim McDonough 2002 - Copyright (C) Luke Howard 2003 + Copyright (C) Andrew Tridgell 1998-2001 + Copyright (C) Andrew Bartlett 2001-2005 + Copyright (C) Jim McDonough 2002 + Copyright (C) Luke Howard 2003 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ #include "auth/auth.h" #include "smb_server/smb_server.h" #include "smbd/service_stream.h" - +#include "libcli/nbt/libnbt.h" /* setup the OS, Lanman and domain portions of a session setup reply @@ -49,9 +49,13 @@ static NTSTATUS sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *s struct auth_usersupplied_info *user_info = NULL; struct auth_serversupplied_info *server_info = NULL; struct auth_session_info *session_info; + struct smbsrv_session *smb_sess; char *remote_machine; TALLOC_CTX *mem_ctx; + sess->old.out.vuid = UID_FIELD_INVALID; + sess->old.out.action = 0; + mem_ctx = talloc_named(req, 0, "OLD session setup"); NT_STATUS_HAVE_NO_MEMORY(mem_ctx); @@ -85,18 +89,25 @@ static NTSTATUS sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *s return auth_nt_status_squash(status); } - sess->old.out.action = 0; - sess->old.out.vuid = smbsrv_register_session(req->smb_conn, session_info, NULL); - if (sess->old.out.vuid == UID_FIELD_INVALID) { + smb_sess = smbsrv_register_session(req->smb_conn, session_info, NULL); + if (!smb_sess) { return NT_STATUS_ACCESS_DENIED; } + + /* Ensure this is marked as a 'real' vuid, not one + * simply valid for the session setup leg */ + smb_sess->finished_sesssetup = True; + + /* To correctly process any AndX packet (like a tree connect) + * we need to fill in the session on the request here */ + req->session = smb_sess; + sess->old.out.vuid = smb_sess->vuid; + sesssetup_common_strings(req, &sess->old.out.os, &sess->old.out.lanman, &sess->old.out.domain); - req->session = smbsrv_session_find(req->smb_conn, sess->old.out.vuid); - return NT_STATUS_OK; } @@ -107,11 +118,15 @@ static NTSTATUS sesssetup_old(struct smbsrv_request *req, union smb_sesssetup *s static NTSTATUS sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *sess) { NTSTATUS status; + struct smbsrv_session *smb_sess; struct auth_usersupplied_info *user_info = NULL; struct auth_serversupplied_info *server_info = NULL; struct auth_session_info *session_info; TALLOC_CTX *mem_ctx; + sess->nt1.out.vuid = UID_FIELD_INVALID; + sess->nt1.out.action = 0; + mem_ctx = talloc_named(req, 0, "NT1 session setup"); NT_STATUS_HAVE_NO_MEMORY(mem_ctx); @@ -143,9 +158,15 @@ static NTSTATUS sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *s status = auth_check_password(auth_context, mem_ctx, user_info, &server_info); } else { - char *remote_machine; + const char *remote_machine = NULL; + + if (req->smb_conn->negotiate.called_name) { + remote_machine = req->smb_conn->negotiate.called_name->name; + } - remote_machine = socket_get_peer_addr(req->smb_conn->connection->socket, mem_ctx); + if (!remote_machine) { + remote_machine = socket_get_peer_addr(req->smb_conn->connection->socket, mem_ctx); + } status = make_user_info_for_reply_enc(req->smb_conn, sess->nt1.in.user, sess->nt1.in.domain, @@ -174,12 +195,21 @@ static NTSTATUS sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *s return auth_nt_status_squash(status); } - sess->nt1.out.action = 0; - sess->nt1.out.vuid = smbsrv_register_session(req->smb_conn, session_info, NULL); + smb_sess = smbsrv_register_session(req->smb_conn, session_info, NULL); talloc_free(mem_ctx); - if (sess->nt1.out.vuid == UID_FIELD_INVALID) { + if (!smb_sess) { return NT_STATUS_ACCESS_DENIED; } + + /* Ensure this is marked as a 'real' vuid, not one + * simply valid for the session setup leg */ + smb_sess->finished_sesssetup = True; + + /* To correctly process any AndX packet (like a tree connect) + * we need to fill in the session on the request here */ + req->session = smb_sess; + sess->nt1.out.vuid = smb_sess->vuid; + sesssetup_common_strings(req, &sess->nt1.out.os, &sess->nt1.out.lanman, @@ -190,7 +220,8 @@ static NTSTATUS sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *s return NT_STATUS_OK; } - if (!srv_setup_signing(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2)) { + + if (!srv_setup_signing(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2)) { /* Already signing, or disabled */ return NT_STATUS_OK; } @@ -198,6 +229,9 @@ static NTSTATUS sesssetup_nt1(struct smbsrv_request *req, union smb_sesssetup *s /* Force check of the request packet, now we know the session key */ req_signing_check_incoming(req); + /* Unfortunetly win2k3 as a client doesn't sign the request + * packet here, so we have to force signing to start again */ + srv_signing_restart(req->smb_conn, &session_info->session_key, &sess->nt1.in.password2); return NT_STATUS_OK; @@ -211,26 +245,29 @@ static NTSTATUS sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup { NTSTATUS status = NT_STATUS_ACCESS_DENIED; struct smbsrv_session *smb_sess; - struct gensec_security *gensec_ctx = NULL; + struct gensec_security *gensec_ctx ; struct auth_session_info *session_info = NULL; uint16_t vuid; + sess->spnego.out.vuid = UID_FIELD_INVALID; + sess->spnego.out.action = 0; + + sesssetup_common_strings(req, + &sess->spnego.out.os, + &sess->spnego.out.lanman, + &sess->spnego.out.workgroup); + if (!req->smb_conn->negotiate.done_sesssetup) { req->smb_conn->negotiate.max_send = sess->nt1.in.bufsize; req->smb_conn->negotiate.client_caps = sess->nt1.in.capabilities; } vuid = SVAL(req->in.hdr,HDR_UID); - smb_sess = smbsrv_session_find(req->smb_conn, vuid); - if (smb_sess && !smb_sess->session_info) { - if (!smb_sess->gensec_ctx) { - return NT_STATUS_INVALID_HANDLE; - } - - status = gensec_update(smb_sess->gensec_ctx, req, sess->spnego.in.secblob, &sess->spnego.out.secblob); + smb_sess = smbsrv_session_find_sesssetup(req->smb_conn, vuid); + if (smb_sess) { + gensec_ctx = smb_sess->gensec_ctx; + status = gensec_update(gensec_ctx, req, sess->spnego.in.secblob, &sess->spnego.out.secblob); } else { - smb_sess = NULL; - status = gensec_server_start(req->smb_conn, &gensec_ctx); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status))); @@ -248,55 +285,55 @@ static NTSTATUS sesssetup_spnego(struct smbsrv_request *req, union smb_sesssetup } status = gensec_update(gensec_ctx, req, sess->spnego.in.secblob, &sess->spnego.out.secblob); - - } - - if (!smb_sess) { - vuid = smbsrv_register_session(req->smb_conn, - session_info, gensec_ctx); - if (vuid == UID_FIELD_INVALID) { - return NT_STATUS_ACCESS_DENIED; - } - smb_sess = smbsrv_session_find(req->smb_conn, vuid); - if (!smb_sess) { - return NT_STATUS_FOOBAR; - } } if (NT_STATUS_IS_OK(status)) { DATA_BLOB session_key; - status = gensec_session_info(smb_sess->gensec_ctx, &smb_sess->session_info); + status = gensec_session_info(gensec_ctx, &session_info); if (!NT_STATUS_IS_OK(status)) { + talloc_free(smb_sess); return status; } - status = gensec_session_key(smb_sess->gensec_ctx, + status = gensec_session_key(gensec_ctx, &session_key); + if (NT_STATUS_IS_OK(status) - && smb_sess->session_info->server_info->authenticated + && session_info->server_info->authenticated && srv_setup_signing(req->smb_conn, &session_key, NULL)) { /* Force check of the request packet, now we know the session key */ req_signing_check_incoming(req); srv_signing_restart(req->smb_conn, &session_key, NULL); - } + + } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { } else { status = auth_nt_status_squash(status); - if (smb_sess->gensec_ctx && - !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - talloc_free(smb_sess->gensec_ctx); - smb_sess->gensec_ctx = NULL; + + /* This invalidates the VUID of the failed login */ + talloc_free(smb_sess); + return status; + } + + if (!smb_sess) { + smb_sess = smbsrv_register_session(req->smb_conn, + session_info, gensec_ctx); + if (!smb_sess) { + return NT_STATUS_ACCESS_DENIED; } + req->session = smb_sess; + } else { + smb_sess->session_info = talloc_reference(smb_sess, session_info); } - sess->spnego.out.action = 0; - sess->spnego.out.vuid = vuid; - sesssetup_common_strings(req, - &sess->spnego.out.os, - &sess->spnego.out.lanman, - &sess->spnego.out.workgroup); + if (NT_STATUS_IS_OK(status)) { + /* Ensure this is marked as a 'real' vuid, not one + * simply valid for the session setup leg */ + smb_sess->finished_sesssetup = True; + } + sess->spnego.out.vuid = smb_sess->vuid; return status; } diff --git a/source4/smb_server/smb_server.c b/source4/smb_server/smb_server.c index 11fe965a92..6261621886 100644 --- a/source4/smb_server/smb_server.c +++ b/source4/smb_server/smb_server.c @@ -737,7 +737,7 @@ static void smbsrv_accept(struct stream_connection *conn) smb_conn->negotiate.zone_offset = get_time_zone(time(NULL)); - smb_conn->sessions.next_vuid = VUID_OFFSET; + smbsrv_vuid_init(smb_conn); srv_init_signing(smb_conn); diff --git a/source4/smb_server/smb_server.h b/source4/smb_server/smb_server.h index 02070f2b13..2f176d9c66 100644 --- a/source4/smb_server/smb_server.h +++ b/source4/smb_server/smb_server.h @@ -47,6 +47,10 @@ struct smbsrv_session { struct gensec_security *gensec_ctx; struct auth_session_info *session_info; + + /* Distinguish between a VUID allocated for the multi-pass + * extended secrity session setup and one that is finished */ + BOOL finished_sesssetup; }; /* we need a forward declaration of the ntvfs_ops strucutre to prevent @@ -216,8 +220,11 @@ struct smbsrv_connection { struct { /* this holds info on session vuids that are already validated for this VC */ struct smbsrv_session *session_list; - uint16_t next_vuid; /* initialise to VUID_OFFSET */ + int num_validated_vuids; + + /* an id tree used to allocate vuids */ + struct idr_context *idtree_vuid; } sessions; /* the server_context holds a linked list of pending requests, -- cgit