X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fsmbd%2Fservice.c;h=8039d16586eb164f576f4c4def3a919aac6feff1;hb=bd269443e311d96ef495a9db47d1b95eb83bb8f4;hp=5b087fd5833c8e8d7adc30e680172fd2dac94525;hpb=f3c477c631e7318ccaa6f277731b721a462112b8;p=samba.git diff --git a/source3/smbd/service.c b/source3/smbd/service.c index 5b087fd5833..8039d16586e 100644 --- a/source3/smbd/service.c +++ b/source3/smbd/service.c @@ -5,7 +5,7 @@ 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 - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -14,36 +14,32 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program. If not, see . */ #include "includes.h" +#include "smbd/globals.h" extern userdom_struct current_user_info; -static BOOL canonicalize_path(connection_struct *conn, pstring path) +static bool canonicalize_connect_path(connection_struct *conn) { #ifdef REALPATH_TAKES_NULL - char *resolved_name = SMB_VFS_REALPATH(conn,path,NULL); + bool ret; + char *resolved_name = SMB_VFS_REALPATH(conn,conn->connectpath,NULL); if (!resolved_name) { - return False; + return false; } - pstrcpy(path, resolved_name); + ret = set_conn_connectpath(conn,resolved_name); SAFE_FREE(resolved_name); - return True; + return ret; #else -#ifdef PATH_MAX char resolved_name_buf[PATH_MAX+1]; -#else - pstring resolved_name_buf; -#endif - char *resolved_name = SMB_VFS_REALPATH(conn,path,resolved_name_buf); + char *resolved_name = SMB_VFS_REALPATH(conn,conn->connectpath,resolved_name_buf); if (!resolved_name) { - return False; + return false; } - pstrcpy(path, resolved_name); - return True; + return set_conn_connectpath(conn,resolved_name); #endif /* REALPATH_TAKES_NULL */ } @@ -53,12 +49,23 @@ static BOOL canonicalize_path(connection_struct *conn, pstring path) Observent people will notice a similarity between this and check_path_syntax :-). ****************************************************************************/ -void set_conn_connectpath(connection_struct *conn, const pstring connectpath) +bool set_conn_connectpath(connection_struct *conn, const char *connectpath) { - pstring destname; - char *d = destname; + char *destname; + char *d; const char *s = connectpath; - BOOL start_of_name_component = True; + bool start_of_name_component = true; + + if (connectpath == NULL || connectpath[0] == '\0') { + return false; + } + + /* Allocate for strlen + '\0' + possible leading '/' */ + destname = SMB_MALLOC(strlen(connectpath) + 2); + if (!destname) { + return false; + } + d = destname; *d++ = '/'; /* Always start with root. */ @@ -143,7 +150,7 @@ void set_conn_connectpath(connection_struct *conn, const pstring connectpath) break; } } - start_of_name_component = False; + start_of_name_component = false; } *d = '\0'; @@ -156,16 +163,16 @@ void set_conn_connectpath(connection_struct *conn, const pstring connectpath) lp_servicename(SNUM(conn)), destname )); string_set(&conn->connectpath, destname); + SAFE_FREE(destname); + return true; } /**************************************************************************** Load parameters specific to a connection/service. ****************************************************************************/ -BOOL set_current_service(connection_struct *conn, uint16 flags, BOOL do_chdir) +bool set_current_service(connection_struct *conn, uint16 flags, bool do_chdir) { - static connection_struct *last_conn; - static uint16 last_flags; int snum; if (!conn) { @@ -180,8 +187,8 @@ BOOL set_current_service(connection_struct *conn, uint16 flags, BOOL do_chdir) if (do_chdir && vfs_ChDir(conn,conn->connectpath) != 0 && vfs_ChDir(conn,conn->origpath) != 0) { - DEBUG(0,("chdir (%s) failed\n", - conn->connectpath)); + DEBUG(((errno!=EACCES)?0:3),("chdir (%s) failed, reason: %s\n", + conn->connectpath, strerror(errno))); return(False); } @@ -216,6 +223,39 @@ BOOL set_current_service(connection_struct *conn, uint16 flags, BOOL do_chdir) return(True); } +static int load_registry_service(const char *servicename) +{ + if (!lp_registry_shares()) { + return -1; + } + + if ((servicename == NULL) || (*servicename == '\0')) { + return -1; + } + + if (strequal(servicename, GLOBAL_NAME)) { + return -2; + } + + if (!process_registry_service(servicename)) { + return -1; + } + + return lp_servicenumber(servicename); +} + +void load_registry_shares(void) +{ + DEBUG(8, ("load_registry_shares()\n")); + if (!lp_registry_shares()) { + return; + } + + process_registry_shares(); + + return; +} + /**************************************************************************** Add a home service. Returns the new service number or -1 if fail. ****************************************************************************/ @@ -224,11 +264,14 @@ int add_home_service(const char *service, const char *username, const char *home { int iHomeService; - if (!service || !homedir) + if (!service || !homedir || homedir[0] == '\0') return -1; - if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0) - return -1; + if ((iHomeService = lp_servicenumber(HOMES_NAME)) < 0) { + if ((iHomeService = load_registry_service(HOMES_NAME)) < 0) { + return -1; + } + } /* * If this is a winbindd provided username, remove @@ -254,105 +297,6 @@ int add_home_service(const char *service, const char *username, const char *home } -static int load_registry_service(const char *servicename) -{ - struct registry_key *key; - char *path; - WERROR err; - - uint32 i; - char *value_name; - struct registry_value *value; - - int res = -1; - - if (!lp_registry_shares()) { - return -1; - } - - if (asprintf(&path, "%s\\%s", KEY_SMBCONF, servicename) == -1) { - return -1; - } - - err = reg_open_path(NULL, path, REG_KEY_READ, get_root_nt_token(), - &key); - SAFE_FREE(path); - - if (!W_ERROR_IS_OK(err)) { - return -1; - } - - res = lp_add_service(servicename, -1); - if (res == -1) { - goto error; - } - - for (i=0; - W_ERROR_IS_OK(reg_enumvalue(key, key, i, &value_name, &value)); - i++) { - switch (value->type) { - case REG_DWORD: { - char *tmp; - if (asprintf(&tmp, "%d", value->v.dword) == -1) { - continue; - } - lp_do_parameter(res, value_name, tmp); - SAFE_FREE(tmp); - break; - } - case REG_SZ: { - lp_do_parameter(res, value_name, value->v.sz.str); - break; - } - default: - /* Ignore all the rest */ - break; - } - - TALLOC_FREE(value_name); - TALLOC_FREE(value); - } - - if (!service_ok(res)) { - /* this is actually never reached, since - * service_ok only returns False if the service - * entry does not have a service name, and we _know_ - * we do have a service name here... */ - res = -1; - } - - error: - - TALLOC_FREE(key); - return res; -} - -void load_registry_shares(void) -{ - struct registry_key *key; - char *name; - WERROR err; - int i; - - if (!lp_registry_shares()) { - return; - } - - err = reg_open_path(NULL, KEY_SMBCONF, REG_KEY_READ, - get_root_nt_token(), &key); - if (!(W_ERROR_IS_OK(err))) { - return; - } - - for (i=0; W_ERROR_IS_OK(reg_enumkey(key, key, i, &name, NULL)); i++) { - load_registry_service(name); - TALLOC_FREE(name); - } - - TALLOC_FREE(key); - return; -} - /** * Find a service entry. * @@ -362,6 +306,7 @@ void load_registry_shares(void) int find_service(fstring service) { int iService; + struct smbd_server_connection *sconn = smbd_server_conn; all_string_sub(service,"\\","/",0); @@ -369,15 +314,16 @@ int find_service(fstring service) /* now handle the special case of a home directory */ if (iService < 0) { - char *phome_dir = get_user_home_dir(service); + char *phome_dir = get_user_home_dir(talloc_tos(), service); if(!phome_dir) { /* * Try mapping the servicename, it may * be a Windows to unix mapped user name. */ - if(map_username(service)) - phome_dir = get_user_home_dir(service); + if(map_username(sconn, service)) + phome_dir = get_user_home_dir( + talloc_tos(), service); } DEBUG(3,("checking for home directory %s gave %s\n",service, @@ -390,7 +336,10 @@ int find_service(fstring service) if (iService < 0) { int iPrinterService; - if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) >= 0) { + if ((iPrinterService = lp_servicenumber(PRINTERS_NAME)) < 0) { + iPrinterService = load_registry_service(PRINTERS_NAME); + } + if (iPrinterService) { DEBUG(3,("checking whether %s is a valid printer name...\n", service)); if (pcap_printername_ok(service)) { DEBUG(3,("%s is a valid printer name\n", service)); @@ -410,6 +359,10 @@ int find_service(fstring service) if (iService < 0) { } + if (iService < 0) { + iService = load_registry_service(service); + } + /* Is it a usershare service ? */ if (iService < 0 && *lp_usershare_path()) { /* Ensure the name is canonicalized. */ @@ -427,13 +380,17 @@ int find_service(fstring service) * could get overwritten by the recursive find_service() call * below. Fix from Josef Hinteregger . */ - pstring defservice; - pstrcpy(defservice, pdefservice); + char *defservice = SMB_STRDUP(pdefservice); + + if (!defservice) { + goto fail; + } /* Disallow anything except explicit share names. */ if (strequal(defservice,HOMES_NAME) || strequal(defservice, PRINTERS_NAME) || strequal(defservice, "IPC$")) { + SAFE_FREE(defservice); goto fail; } @@ -442,13 +399,10 @@ int find_service(fstring service) all_string_sub(service, "_","/",0); iService = lp_add_service(service, iService); } + SAFE_FREE(defservice); } } - if (iService < 0) { - iService = load_registry_service(service); - } - if (iService >= 0) { if (!VALID_SNUM(iService)) { DEBUG(0,("Invalid snum %d for %s\n",iService, service)); @@ -511,31 +465,6 @@ static NTSTATUS share_sanity_checks(int snum, fstring dev) return NT_STATUS_OK; } -static NTSTATUS find_forced_user(connection_struct *conn, BOOL vuser_is_guest, fstring username) -{ - int snum = conn->params->service; - char *fuser, *found_username; - NTSTATUS result; - - if (!(fuser = talloc_string_sub(conn->mem_ctx, lp_force_user(snum), "%S", - lp_servicename(snum)))) { - return NT_STATUS_NO_MEMORY; - } - - result = create_token_from_username(conn->mem_ctx, fuser, vuser_is_guest, - &conn->uid, &conn->gid, &found_username, - &conn->nt_user_token); - if (!NT_STATUS_IS_OK(result)) { - return result; - } - - fstrcpy(username, found_username); - - TALLOC_FREE(fuser); - TALLOC_FREE(found_username); - return NT_STATUS_OK; -} - /* * Go through lookup_name etc to find the force'd group. * @@ -543,29 +472,20 @@ static NTSTATUS find_forced_user(connection_struct *conn, BOOL vuser_is_guest, f * one found. */ -static NTSTATUS find_forced_group(BOOL force_user, +static NTSTATUS find_forced_group(bool force_user, int snum, const char *username, DOM_SID *pgroup_sid, gid_t *pgid) { NTSTATUS result = NT_STATUS_NO_SUCH_GROUP; - TALLOC_CTX *mem_ctx; + TALLOC_CTX *frame = talloc_stackframe(); DOM_SID group_sid; enum lsa_SidType type; char *groupname; - BOOL user_must_be_member = False; + bool user_must_be_member = False; gid_t gid; - ZERO_STRUCTP(pgroup_sid); - *pgid = (gid_t)-1; - - mem_ctx = talloc_new(NULL); - if (mem_ctx == NULL) { - DEBUG(0, ("talloc_new failed\n")); - return NT_STATUS_NO_MEMORY; - } - - groupname = talloc_strdup(mem_ctx, lp_force_group(snum)); + groupname = talloc_strdup(talloc_tos(), lp_force_group(snum)); if (groupname == NULL) { DEBUG(1, ("talloc_strdup failed\n")); result = NT_STATUS_NO_MEMORY; @@ -577,10 +497,15 @@ static NTSTATUS find_forced_group(BOOL force_user, groupname += 1; } - groupname = talloc_string_sub(mem_ctx, groupname, + groupname = talloc_string_sub(talloc_tos(), groupname, "%S", lp_servicename(snum)); + if (groupname == NULL) { + DEBUG(1, ("talloc_string_sub failed\n")); + result = NT_STATUS_NO_MEMORY; + goto done; + } - if (!lookup_name_smbconf(mem_ctx, groupname, + if (!lookup_name_smbconf(talloc_tos(), groupname, LOOKUP_NAME_ALL|LOOKUP_NAME_GROUP, NULL, NULL, &group_sid, &type)) { DEBUG(10, ("lookup_name_smbconf(%s) failed\n", @@ -597,7 +522,7 @@ static NTSTATUS find_forced_group(BOOL force_user, if (!sid_to_gid(&group_sid, &gid)) { DEBUG(10, ("sid_to_gid(%s) for %s failed\n", - sid_string_static(&group_sid), groupname)); + sid_string_dbg(&group_sid), groupname)); goto done; } @@ -629,151 +554,149 @@ static NTSTATUS find_forced_group(BOOL force_user, result = NT_STATUS_OK; done: - TALLOC_FREE(mem_ctx); + TALLOC_FREE(frame); return result; } +/**************************************************************************** + Create an auth_serversupplied_info structure for a connection_struct +****************************************************************************/ + +static NTSTATUS create_connection_server_info(struct smbd_server_connection *sconn, + TALLOC_CTX *mem_ctx, int snum, + struct auth_serversupplied_info *vuid_serverinfo, + DATA_BLOB password, + struct auth_serversupplied_info **presult) +{ + if (lp_guest_only(snum)) { + return make_server_info_guest(mem_ctx, presult); + } + + if (vuid_serverinfo != NULL) { + + struct auth_serversupplied_info *result; + + /* + * This is the normal security != share case where we have a + * valid vuid from the session setup. */ + + if (vuid_serverinfo->guest) { + if (!lp_guest_ok(snum)) { + DEBUG(2, ("guest user (from session setup) " + "not permitted to access this share " + "(%s)\n", lp_servicename(snum))); + return NT_STATUS_ACCESS_DENIED; + } + } else { + if (!user_ok_token(vuid_serverinfo->unix_name, + pdb_get_domain(vuid_serverinfo->sam_account), + vuid_serverinfo->ptok, snum)) { + DEBUG(2, ("user '%s' (from session setup) not " + "permitted to access this share " + "(%s)\n", + vuid_serverinfo->unix_name, + lp_servicename(snum))); + return NT_STATUS_ACCESS_DENIED; + } + } + + result = copy_serverinfo(mem_ctx, vuid_serverinfo); + if (result == NULL) { + return NT_STATUS_NO_MEMORY; + } + + *presult = result; + return NT_STATUS_OK; + } + + if (lp_security() == SEC_SHARE) { + + fstring user; + bool guest; + + /* add the sharename as a possible user name if we + are in share mode security */ + + add_session_user(sconn, lp_servicename(snum)); + + /* shall we let them in? */ + + if (!authorise_login(sconn, snum,user,password,&guest)) { + DEBUG( 2, ( "Invalid username/password for [%s]\n", + lp_servicename(snum)) ); + return NT_STATUS_WRONG_PASSWORD; + } + + return make_serverinfo_from_username(mem_ctx, user, guest, + presult); + } + + DEBUG(0, ("invalid VUID (vuser) but not in security=share\n")); + return NT_STATUS_ACCESS_DENIED; +} + + /**************************************************************************** Make a connection, given the snum to connect to, and the vuser of the connecting user if appropriate. ****************************************************************************/ -static connection_struct *make_connection_snum(int snum, user_struct *vuser, - DATA_BLOB password, - const char *pdev, - NTSTATUS *status) +connection_struct *make_connection_snum(struct smbd_server_connection *sconn, + int snum, user_struct *vuser, + DATA_BLOB password, + const char *pdev, + NTSTATUS *pstatus) { - struct passwd *pass = NULL; - BOOL guest = False; connection_struct *conn; - SMB_STRUCT_STAT st; - fstring user; + struct smb_filename *smb_fname_cpath = NULL; fstring dev; int ret; + char addr[INET6_ADDRSTRLEN]; + bool on_err_call_dis_hook = false; + NTSTATUS status; - *user = 0; fstrcpy(dev, pdev); - SET_STAT_INVALID(st); - if (NT_STATUS_IS_ERR(*status = share_sanity_checks(snum, dev))) { + if (NT_STATUS_IS_ERR(*pstatus = share_sanity_checks(snum, dev))) { return NULL; } - conn = conn_new(); + conn = conn_new(sconn); if (!conn) { DEBUG(0,("Couldn't find free connection.\n")); - *status = NT_STATUS_INSUFFICIENT_RESOURCES; + *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES; return NULL; } conn->params->service = snum; - conn->nt_user_token = NULL; - - if (lp_guest_only(snum)) { - const char *guestname = lp_guestaccount(); - NTSTATUS status2; - char *found_username = NULL; - - guest = True; - pass = getpwnam_alloc(NULL, guestname); - if (!pass) { - DEBUG(0,("make_connection_snum: Invalid guest " - "account %s??\n",guestname)); - conn_free(conn); - *status = NT_STATUS_NO_SUCH_USER; - return NULL; - } - status2 = create_token_from_username(conn->mem_ctx, pass->pw_name, True, - &conn->uid, &conn->gid, - &found_username, - &conn->nt_user_token); - if (!NT_STATUS_IS_OK(status2)) { - TALLOC_FREE(pass); - conn_free(conn); - *status = status2; - return NULL; - } - fstrcpy(user, found_username); - string_set(&conn->user,user); - conn->force_user = True; - TALLOC_FREE(found_username); - TALLOC_FREE(pass); - DEBUG(3,("Guest only user %s\n",user)); - } else if (vuser) { - if (vuser->guest) { - if (!lp_guest_ok(snum)) { - DEBUG(2, ("guest user (from session setup) " - "not permitted to access this share " - "(%s)\n", lp_servicename(snum))); - conn_free(conn); - *status = NT_STATUS_ACCESS_DENIED; - return NULL; - } - } else { - if (!user_ok_token(vuser->user.unix_name, - vuser->nt_user_token, snum)) { - DEBUG(2, ("user '%s' (from session setup) not " - "permitted to access this share " - "(%s)\n", vuser->user.unix_name, - lp_servicename(snum))); - conn_free(conn); - *status = NT_STATUS_ACCESS_DENIED; - return NULL; - } - } - conn->vuid = vuser->vuid; - conn->uid = vuser->uid; - conn->gid = vuser->gid; - string_set(&conn->user,vuser->user.unix_name); - fstrcpy(user,vuser->user.unix_name); - guest = vuser->guest; - } else if (lp_security() == SEC_SHARE) { - NTSTATUS status2; - char *found_username = NULL; - - /* add it as a possible user name if we - are in share mode security */ - add_session_user(lp_servicename(snum)); - /* shall we let them in? */ - if (!authorise_login(snum,user,password,&guest)) { - DEBUG( 2, ( "Invalid username/password for [%s]\n", - lp_servicename(snum)) ); - conn_free(conn); - *status = NT_STATUS_WRONG_PASSWORD; - return NULL; - } - pass = Get_Pwnam(user); - status2 = create_token_from_username(conn->mem_ctx, pass->pw_name, True, - &conn->uid, &conn->gid, - &found_username, - &conn->nt_user_token); - if (!NT_STATUS_IS_OK(status2)) { - conn_free(conn); - *status = status2; - return NULL; - } - fstrcpy(user, found_username); - string_set(&conn->user,user); - TALLOC_FREE(found_username); - conn->force_user = True; - } else { - DEBUG(0, ("invalid VUID (vuser) but not in security=share\n")); + + status = create_connection_server_info(sconn, + conn, snum, vuser ? vuser->server_info : NULL, password, + &conn->server_info); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("create_connection_server_info failed: %s\n", + nt_errstr(status))); + *pstatus = status; conn_free(conn); - *status = NT_STATUS_ACCESS_DENIED; return NULL; } - add_session_user(user); + if ((lp_guest_only(snum)) || (lp_security() == SEC_SHARE)) { + conn->force_user = true; + } + + add_session_user(sconn, conn->server_info->unix_name); - safe_strcpy(conn->client_address, client_addr(), - sizeof(conn->client_address)-1); + safe_strcpy(conn->client_address, + client_addr(get_client_fd(),addr,sizeof(addr)), + sizeof(conn->client_address)-1); conn->num_files_open = 0; conn->lastused = conn->lastused_count = time(NULL); conn->used = True; conn->printer = (strncmp(dev,"LPT",3) == 0); conn->ipc = ( (strncmp(dev,"IPC",3) == 0) || ( lp_enable_asu_support() && strequal(dev,"ADMIN$")) ); - conn->dirptr = NULL; /* Case options for the share. */ if (lp_casesensitive(snum) == Auto) { @@ -781,130 +704,109 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, * insensitive for now. */ conn->case_sensitive = False; } else { - conn->case_sensitive = (BOOL)lp_casesensitive(snum); + conn->case_sensitive = (bool)lp_casesensitive(snum); } conn->case_preserve = lp_preservecase(snum); conn->short_case_preserve = lp_shortpreservecase(snum); + conn->encrypt_level = lp_smb_encrypt(snum); + conn->veto_list = NULL; conn->hide_list = NULL; conn->veto_oplock_list = NULL; - string_set(&conn->dirpath,""); - string_set(&conn->user,user); + conn->aio_write_behind_list = NULL; conn->read_only = lp_readonly(SNUM(conn)); conn->admin_user = False; - /* - * If force user is true, then store the given userid and the gid of - * the user we're forcing. - * For auxiliary groups see below. - */ - if (*lp_force_user(snum)) { - NTSTATUS status2; - status2 = find_forced_user(conn, - (vuser != NULL) && vuser->guest, - user); - if (!NT_STATUS_IS_OK(status2)) { + /* + * Replace conn->server_info with a completely faked up one + * from the username we are forced into :-) + */ + + char *fuser; + struct auth_serversupplied_info *forced_serverinfo; + + fuser = talloc_string_sub(conn, lp_force_user(snum), "%S", + lp_servicename(snum)); + if (fuser == NULL) { conn_free(conn); - *status = status2; + *pstatus = NT_STATUS_NO_MEMORY; return NULL; } - string_set(&conn->user,user); + + status = make_serverinfo_from_username( + conn, fuser, conn->server_info->guest, + &forced_serverinfo); + if (!NT_STATUS_IS_OK(status)) { + conn_free(conn); + *pstatus = status; + return NULL; + } + + TALLOC_FREE(conn->server_info); + conn->server_info = forced_serverinfo; + conn->force_user = True; - DEBUG(3,("Forced user %s\n",user)); + DEBUG(3,("Forced user %s\n", fuser)); } /* * If force group is true, then override * any groupid stored for the connecting user. */ - + if (*lp_force_group(snum)) { - NTSTATUS status2; - DOM_SID group_sid; - status2 = find_forced_group(conn->force_user, - snum, user, - &group_sid, &conn->gid); - if (!NT_STATUS_IS_OK(status2)) { + status = find_forced_group( + conn->force_user, snum, conn->server_info->unix_name, + &conn->server_info->ptok->user_sids[1], + &conn->server_info->utok.gid); + + if (!NT_STATUS_IS_OK(status)) { conn_free(conn); - *status = status2; + *pstatus = status; return NULL; } - if ((conn->nt_user_token == NULL) && (vuser != NULL)) { - - /* Not force user and not security=share, but force - * group. vuser has a token to copy */ - - conn->nt_user_token = dup_nt_token( - NULL, vuser->nt_user_token); - if (conn->nt_user_token == NULL) { - DEBUG(0, ("dup_nt_token failed\n")); - conn_free(conn); - *status = NT_STATUS_NO_MEMORY; - return NULL; - } - } - - /* If conn->nt_user_token is still NULL, we have - * security=share. This means ignore the SID, as we had no - * vuser to copy from */ - - if (conn->nt_user_token != NULL) { - /* Overwrite the primary group sid */ - sid_copy(&conn->nt_user_token->user_sids[1], - &group_sid); - - } - conn->force_group = True; + /* + * We need to cache this gid, to use within + * change_to_user() separately from the conn->server_info + * struct. We only use conn->server_info directly if + * "force_user" was set. + */ + conn->force_group_gid = conn->server_info->utok.gid; } - if (conn->nt_user_token != NULL) { - size_t i; - - /* We have a share-specific token from force [user|group]. - * This means we have to create the list of unix groups from - * the list of sids. */ - - conn->ngroups = 0; - conn->groups = NULL; + conn->vuid = (vuser != NULL) ? vuser->vuid : UID_FIELD_INVALID; - for (i=0; int_user_token->num_sids; i++) { - gid_t gid; - DOM_SID *sid = &conn->nt_user_token->user_sids[i]; - - if (!sid_to_gid(sid, &gid)) { - DEBUG(10, ("Could not convert SID %s to gid, " - "ignoring it\n", - sid_string_static(sid))); - continue; - } - if (!add_gid_to_array_unique(conn->mem_ctx, gid, &conn->groups, - &conn->ngroups)) { - DEBUG(0, ("add_gid_to_array_unique failed\n")); - conn_free(conn); - *status = NT_STATUS_NO_MEMORY; - return NULL; - } + { + char *s = talloc_sub_advanced(talloc_tos(), + lp_servicename(SNUM(conn)), + conn->server_info->unix_name, + conn->connectpath, + conn->server_info->utok.gid, + conn->server_info->sanitized_username, + pdb_get_domain(conn->server_info->sam_account), + lp_pathname(snum)); + if (!s) { + conn_free(conn); + *pstatus = NT_STATUS_NO_MEMORY; + return NULL; } - } - { - pstring s; - pstrcpy(s,lp_pathname(snum)); - standard_sub_advanced(lp_servicename(SNUM(conn)), conn->user, - conn->connectpath, conn->gid, - get_current_username(), - current_user_info.domain, - s, sizeof(s)); - set_conn_connectpath(conn,s); + if (!set_conn_connectpath(conn,s)) { + TALLOC_FREE(s); + conn_free(conn); + *pstatus = NT_STATUS_NO_MEMORY; + return NULL; + } DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(snum))); + TALLOC_FREE(s); } /* @@ -915,33 +817,14 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, */ { - BOOL can_write = False; - NT_USER_TOKEN *token = conn->nt_user_token ? - conn->nt_user_token : - (vuser ? vuser->nt_user_token : NULL); + bool can_write = False; - /* - * I don't believe this can happen. But the - * logic above is convoluted enough to confuse - * automated checkers, so be sure. JRA. - */ - - if (token == NULL) { - DEBUG(0,("make_connection: connection to %s " - "denied due to missing " - "NT token.\n", - lp_servicename(snum))); - conn_free(conn); - *status = NT_STATUS_ACCESS_DENIED; - return NULL; - } - - can_write = share_access_check(token, - lp_servicename(snum), - FILE_WRITE_DATA); + can_write = share_access_check(conn->server_info->ptok, + lp_servicename(snum), + FILE_WRITE_DATA); if (!can_write) { - if (!share_access_check(token, + if (!share_access_check(conn->server_info->ptok, lp_servicename(snum), FILE_READ_DATA)) { /* No access, read or write. */ @@ -950,7 +833,7 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, "descriptor.\n", lp_servicename(snum))); conn_free(conn); - *status = NT_STATUS_ACCESS_DENIED; + *pstatus = NT_STATUS_ACCESS_DENIED; return NULL; } else { conn->read_only = True; @@ -963,7 +846,7 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(snum))); conn_free(conn); - *status = NT_STATUS_BAD_NETWORK_NAME; + *pstatus = NT_STATUS_BAD_NETWORK_NAME; return NULL; } @@ -975,28 +858,47 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, * depend on the realpath() pointer in the vfs table. JRA. */ if (!lp_widelinks(snum)) { - pstring s; - pstrcpy(s,conn->connectpath); - canonicalize_path(conn, s); - set_conn_connectpath(conn,s); + if (!canonicalize_connect_path(conn)) { + DEBUG(0, ("canonicalize_connect_path failed " + "for service %s, path %s\n", + lp_servicename(snum), + conn->connectpath)); + conn_free(conn); + *pstatus = NT_STATUS_BAD_NETWORK_NAME; + return NULL; + } } if ((!conn->printer) && (!conn->ipc)) { - conn->notify_ctx = notify_init(conn->mem_ctx, server_id_self(), + conn->notify_ctx = notify_init(conn, server_id_self(), smbd_messaging_context(), smbd_event_context(), conn); } /* ROOT Activities: */ - /* check number of connections */ - if (!claim_connection(conn, - lp_servicename(snum), - lp_max_connections(snum), - 0)) { - DEBUG(1,("too many connections - rejected\n")); + /* + * Enforce the max connections parameter. + */ + + if ((lp_max_connections(snum) > 0) + && (count_current_connections(lp_servicename(SNUM(conn)), True) >= + lp_max_connections(snum))) { + + DEBUG(1, ("Max connections (%d) exceeded for %s\n", + lp_max_connections(snum), lp_servicename(snum))); conn_free(conn); - *status = NT_STATUS_INSUFFICIENT_RESOURCES; + *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES; + return NULL; + } + + /* + * Get us an entry in the connections db + */ + if (!claim_connection(conn, lp_servicename(snum), 0)) { + DEBUG(1, ("Could not store connections entry\n")); + conn_free(conn); + *pstatus = NT_STATUS_INTERNAL_DB_ERROR; return NULL; } @@ -1004,21 +906,23 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, * to below */ /* execute any "root preexec = " line */ if (*lp_rootpreexec(snum)) { - pstring cmd; - pstrcpy(cmd,lp_rootpreexec(snum)); - standard_sub_advanced(lp_servicename(SNUM(conn)), conn->user, - conn->connectpath, conn->gid, - get_current_username(), - current_user_info.domain, - cmd, sizeof(cmd)); + char *cmd = talloc_sub_advanced(talloc_tos(), + lp_servicename(SNUM(conn)), + conn->server_info->unix_name, + conn->connectpath, + conn->server_info->utok.gid, + conn->server_info->sanitized_username, + pdb_get_domain(conn->server_info->sam_account), + lp_rootpreexec(snum)); DEBUG(5,("cmd=%s\n",cmd)); ret = smbrun(cmd,NULL); + TALLOC_FREE(cmd); if (ret != 0 && lp_rootpreexec_close(snum)) { DEBUG(1,("root preexec gave %d - failing " "connection\n", ret)); yield_connection(conn, lp_servicename(snum)); conn_free(conn); - *status = NT_STATUS_ACCESS_DENIED; + *pstatus = NT_STATUS_ACCESS_DENIED; return NULL; } } @@ -1029,7 +933,7 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, DEBUG(0,("Can't become connected user!\n")); yield_connection(conn, lp_servicename(snum)); conn_free(conn); - *status = NT_STATUS_LOGON_FAILURE; + *pstatus = NT_STATUS_LOGON_FAILURE; return NULL; } @@ -1041,22 +945,21 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, /* execute any "preexec = " line */ if (*lp_preexec(snum)) { - pstring cmd; - pstrcpy(cmd,lp_preexec(snum)); - standard_sub_advanced(lp_servicename(SNUM(conn)), conn->user, - conn->connectpath, conn->gid, - get_current_username(), - current_user_info.domain, - cmd, sizeof(cmd)); + char *cmd = talloc_sub_advanced(talloc_tos(), + lp_servicename(SNUM(conn)), + conn->server_info->unix_name, + conn->connectpath, + conn->server_info->utok.gid, + conn->server_info->sanitized_username, + pdb_get_domain(conn->server_info->sam_account), + lp_preexec(snum)); ret = smbrun(cmd,NULL); + TALLOC_FREE(cmd); if (ret != 0 && lp_preexec_close(snum)) { DEBUG(1,("preexec gave %d - failing connection\n", ret)); - change_to_root_user(); - yield_connection(conn, lp_servicename(snum)); - conn_free(conn); - *status = NT_STATUS_ACCESS_DENIED; - return NULL; + *pstatus = NT_STATUS_ACCESS_DENIED; + goto err_root_exit; } } @@ -1071,19 +974,29 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, set_namearray( &conn->veto_list, lp_veto_files(snum)); set_namearray( &conn->hide_list, lp_hide_files(snum)); set_namearray( &conn->veto_oplock_list, lp_veto_oplocks(snum)); + set_namearray( &conn->aio_write_behind_list, + lp_aio_write_behind(snum)); } /* Invoke VFS make connection hook - do this before the VFS_STAT call to allow any filesystems needing user credentials to initialize themselves. */ - if (SMB_VFS_CONNECT(conn, lp_servicename(snum), user) < 0) { + if (SMB_VFS_CONNECT(conn, lp_servicename(snum), + conn->server_info->unix_name) < 0) { DEBUG(0,("make_connection: VFS make connection failed!\n")); - change_to_root_user(); - yield_connection(conn, lp_servicename(snum)); - conn_free(conn); - *status = NT_STATUS_UNSUCCESSFUL; - return NULL; + *pstatus = NT_STATUS_UNSUCCESSFUL; + goto err_root_exit; + } + + /* Any error exit after here needs to call the disconnect hook. */ + on_err_call_dis_hook = true; + + status = create_synthetic_smb_fname(talloc_tos(), conn->connectpath, + NULL, NULL, &smb_fname_cpath); + if (!NT_STATUS_IS_OK(status)) { + *pstatus = status; + goto err_root_exit; } /* win2000 does not check the permissions on the directory @@ -1091,9 +1004,9 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, check during individual operations. To match this behaviour I have disabled this chdir check (tridge) */ /* the alternative is just to check the directory exists */ - if ((ret = SMB_VFS_STAT(conn, conn->connectpath, &st)) != 0 || - !S_ISDIR(st.st_mode)) { - if (ret == 0 && !S_ISDIR(st.st_mode)) { + if ((ret = SMB_VFS_STAT(conn, smb_fname_cpath)) != 0 || + !S_ISDIR(smb_fname_cpath->st.st_ex_mode)) { + if (ret == 0 && !S_ISDIR(smb_fname_cpath->st.st_ex_mode)) { DEBUG(0,("'%s' is not a directory, when connecting to " "[%s]\n", conn->connectpath, lp_servicename(snum))); @@ -1103,28 +1016,44 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, conn->connectpath, lp_servicename(snum), strerror(errno) )); } - change_to_root_user(); - /* Call VFS disconnect hook */ - SMB_VFS_DISCONNECT(conn); - yield_connection(conn, lp_servicename(snum)); - conn_free(conn); - *status = NT_STATUS_BAD_NETWORK_NAME; - return NULL; + *pstatus = NT_STATUS_BAD_NETWORK_NAME; + goto err_root_exit; } - + string_set(&conn->origpath,conn->connectpath); - + #if SOFTLINK_OPTIMISATION /* resolve any soft links early if possible */ if (vfs_ChDir(conn,conn->connectpath) == 0) { - pstring s; - pstrcpy(s,conn->connectpath); - vfs_GetWd(conn,s); - set_conn_connectpath(conn,s); + TALLOC_CTX *ctx = talloc_tos(); + char *s = vfs_GetWd(ctx,s); + if (!s) { + *status = map_nt_error_from_unix(errno); + goto err_root_exit; + } + if (!set_conn_connectpath(conn,s)) { + *status = NT_STATUS_NO_MEMORY; + goto err_root_exit; + } vfs_ChDir(conn,conn->connectpath); } #endif - + + if (lp_unix_extensions() && lp_widelinks(snum)) { + DEBUG(0,("Share '%s' has wide links and unix extensions enabled. " + "These parameters are incompatible. " + "Disabling wide links for this share.\n", + lp_servicename(snum) )); + lp_do_parameter(snum, "wide links", "False"); + } + + /* Figure out the characteristics of the underlying filesystem. This + * assumes that all the filesystem mounted withing a share path have + * the same characteristics, which is likely but not guaranteed. + */ + + conn->fs_capabilities = SMB_VFS_FS_CAPABILITIES(conn, &conn->ts_res); + /* * Print out the 'connected as' stuff here as we need * to know the effective uid and gid we will be using @@ -1134,48 +1063,28 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address ); - dbgtext( "%s", srv_is_signing_active() ? "signed " : ""); + dbgtext( "%s", srv_is_signing_active(smbd_server_conn) ? "signed " : ""); dbgtext( "connect to service %s ", lp_servicename(snum) ); - dbgtext( "initially as user %s ", user ); + dbgtext( "initially as user %s ", + conn->server_info->unix_name ); dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() ); dbgtext( "(pid %d)\n", (int)sys_getpid() ); } - + /* we've finished with the user stuff - go back to root */ change_to_root_user(); return(conn); -} -/*************************************************************************************** - Simple wrapper function for make_connection() to include a call to - vfs_chdir() - **************************************************************************************/ - -connection_struct *make_connection_with_chdir(const char *service_in, - DATA_BLOB password, - const char *dev, uint16 vuid, - NTSTATUS *status) -{ - connection_struct *conn = NULL; - - conn = make_connection(service_in, password, dev, vuid, status); - - /* - * make_connection() does not change the directory for us any more - * so we have to do it as a separate step --jerry - */ - - if ( conn && vfs_ChDir(conn,conn->connectpath) != 0 ) { - DEBUG(0,("move_driver_to_download_area: Can't change " - "directory to %s for [print$] (%s)\n", - conn->connectpath,strerror(errno))); - yield_connection(conn, lp_servicename(SNUM(conn))); - conn_free(conn); - *status = NT_STATUS_UNSUCCESSFUL; - return NULL; + err_root_exit: + TALLOC_FREE(smb_fname_cpath); + change_to_root_user(); + if (on_err_call_dis_hook) { + /* Call VFS disconnect hook */ + SMB_VFS_DISCONNECT(conn); } - - return conn; + yield_connection(conn, lp_servicename(snum)); + conn_free(conn); + return NULL; } /**************************************************************************** @@ -1184,7 +1093,8 @@ connection_struct *make_connection_with_chdir(const char *service_in, * @param service ****************************************************************************/ -connection_struct *make_connection(const char *service_in, DATA_BLOB password, +connection_struct *make_connection(struct smbd_server_connection *sconn, + const char *service_in, DATA_BLOB password, const char *pdev, uint16 vuid, NTSTATUS *status) { @@ -1193,6 +1103,7 @@ connection_struct *make_connection(const char *service_in, DATA_BLOB password, fstring service; fstring dev; int snum = -1; + char addr[INET6_ADDRSTRLEN]; fstrcpy(dev, pdev); @@ -1204,13 +1115,13 @@ connection_struct *make_connection(const char *service_in, DATA_BLOB password, smb_panic("make_connection: PANIC ERROR. Called as nonroot\n"); } - if (conn_num_open() > 2047) { + if (conn_num_open(sconn) > 2047) { *status = NT_STATUS_INSUFF_SERVER_RESOURCES; return NULL; } if(lp_security() != SEC_SHARE) { - vuser = get_valid_user_struct(vuid); + vuser = get_valid_user_struct(sconn, vuid); if (!vuser) { DEBUG(1,("make_connection: refusing to connect with " "no session setup\n")); @@ -1241,7 +1152,8 @@ connection_struct *make_connection(const char *service_in, DATA_BLOB password, } DEBUG(5, ("making a connection to [homes] service " "created at session setup time\n")); - return make_connection_snum(vuser->homes_snum, + return make_connection_snum(sconn, + vuser->homes_snum, vuser, no_pw, dev, status); } else { @@ -1251,14 +1163,15 @@ connection_struct *make_connection(const char *service_in, DATA_BLOB password, fstring unix_username; fstrcpy(unix_username, current_user_info.smb_name); - map_username(unix_username); + map_username(sconn, unix_username); snum = find_service(unix_username); } if (snum != -1) { DEBUG(5, ("making a connection to 'homes' " "service %s based on " "security=share\n", service_in)); - return make_connection_snum(snum, NULL, + return make_connection_snum(sconn, + snum, NULL, password, dev, status); } @@ -1269,7 +1182,8 @@ connection_struct *make_connection(const char *service_in, DATA_BLOB password, DATA_BLOB no_pw = data_blob_null; DEBUG(5, ("making a connection to 'homes' service [%s] " "created at session setup time\n", service_in)); - return make_connection_snum(vuser->homes_snum, + return make_connection_snum(sconn, + vuser->homes_snum, vuser, no_pw, dev, status); } @@ -1288,8 +1202,10 @@ connection_struct *make_connection(const char *service_in, DATA_BLOB password, return NULL; } - DEBUG(0,("%s (%s) couldn't find service %s\n", - get_remote_machine_name(), client_addr(), service)); + DEBUG(3,("%s (%s) couldn't find service %s\n", + get_remote_machine_name(), + client_addr(get_client_fd(),addr,sizeof(addr)), + service)); *status = NT_STATUS_BAD_NETWORK_NAME; return NULL; } @@ -1305,7 +1221,7 @@ connection_struct *make_connection(const char *service_in, DATA_BLOB password, DEBUG(5, ("making a connection to 'normal' service %s\n", service)); - return make_connection_snum(snum, vuser, + return make_connection_snum(sconn, snum, vuser, password, dev, status); } @@ -1316,10 +1232,9 @@ connection_struct *make_connection(const char *service_in, DATA_BLOB password, void close_cnum(connection_struct *conn, uint16 vuid) { - if (IS_IPC(conn)) { - pipe_close_conn(conn); - } else { - file_close_conn(conn); + file_close_conn(conn); + + if (!IS_IPC(conn)) { dptr_closecnum(conn); } @@ -1341,28 +1256,32 @@ void close_cnum(connection_struct *conn, uint16 vuid) /* execute any "postexec = " line */ if (*lp_postexec(SNUM(conn)) && change_to_user(conn, vuid)) { - pstring cmd; - pstrcpy(cmd,lp_postexec(SNUM(conn))); - standard_sub_advanced(lp_servicename(SNUM(conn)), conn->user, - conn->connectpath, conn->gid, - get_current_username(), - current_user_info.domain, - cmd, sizeof(cmd)); + char *cmd = talloc_sub_advanced(talloc_tos(), + lp_servicename(SNUM(conn)), + conn->server_info->unix_name, + conn->connectpath, + conn->server_info->utok.gid, + conn->server_info->sanitized_username, + pdb_get_domain(conn->server_info->sam_account), + lp_postexec(SNUM(conn))); smbrun(cmd,NULL); + TALLOC_FREE(cmd); change_to_root_user(); } change_to_root_user(); /* execute any "root postexec = " line */ if (*lp_rootpostexec(SNUM(conn))) { - pstring cmd; - pstrcpy(cmd,lp_rootpostexec(SNUM(conn))); - standard_sub_advanced(lp_servicename(SNUM(conn)), conn->user, - conn->connectpath, conn->gid, - get_current_username(), - current_user_info.domain, - cmd, sizeof(cmd)); + char *cmd = talloc_sub_advanced(talloc_tos(), + lp_servicename(SNUM(conn)), + conn->server_info->unix_name, + conn->connectpath, + conn->server_info->utok.gid, + conn->server_info->sanitized_username, + pdb_get_domain(conn->server_info->sam_account), + lp_rootpostexec(SNUM(conn))); smbrun(cmd,NULL); + TALLOC_FREE(cmd); } conn_free(conn);