connection_struct *make_connection_snum(struct smbd_server_connection *sconn, int snum, user_struct *vuser, DATA_BLOB password, const char *pdev, NTSTATUS *pstatus) { connection_struct *conn; struct smb_filename *smb_fname_cpath = NULL; fstring dev; int ret; char addr[INET6_ADDRSTRLEN]; bool on_err_call_dis_hook = false; NTSTATUS status; fstrcpy(dev, pdev); if (NT_STATUS_IS_ERR(*pstatus = share_sanity_checks(snum, dev))) { return NULL; } conn = conn_new(sconn); if (!conn) { DEBUG(0,("Couldn't find free connection.\n")); *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES; return NULL; } conn->params->service = snum; 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); return NULL; } 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(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$")) ); /* Case options for the share. */ if (lp_casesensitive(snum) == Auto) { /* We will be setting this per packet. Set to be case * insensitive for now. */ conn->case_sensitive = False; } else { 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; conn->aio_write_behind_list = NULL; conn->read_only = lp_readonly(SNUM(conn)); conn->admin_user = False; if (*lp_force_user(snum)) { /* * 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); *pstatus = NT_STATUS_NO_MEMORY; return NULL; } 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", fuser)); } /* * If force group is true, then override * any groupid stored for the connecting user. */ if (*lp_force_group(snum)) { 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); *pstatus = status; return NULL; } /* * 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; } conn->vuid = (vuser != NULL) ? vuser->vuid : UID_FIELD_INVALID; { 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; } 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); } /* * New code to check if there's a share security descripter * added from NT server manager. This is done after the * smb.conf checks are done as we need a uid and token. JRA. * */ { bool can_write = False; can_write = share_access_check(conn->server_info->ptok, lp_servicename(snum), FILE_WRITE_DATA); if (!can_write) { if (!share_access_check(conn->server_info->ptok, lp_servicename(snum), FILE_READ_DATA)) { /* No access, read or write. */ DEBUG(0,("make_connection: connection to %s " "denied due to security " "descriptor.\n", lp_servicename(snum))); conn_free(conn); *pstatus = NT_STATUS_ACCESS_DENIED; return NULL; } else { conn->read_only = True; } } } /* Initialise VFS function pointers */ if (!smbd_vfs_init(conn)) { DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(snum))); conn_free(conn); *pstatus = NT_STATUS_BAD_NETWORK_NAME; return NULL; } /* * If widelinks are disallowed we need to canonicalise the connect * path here to ensure we don't have any symlinks in the * connectpath. We will be checking all paths on this connection are * below this directory. We must do this after the VFS init as we * depend on the realpath() pointer in the vfs table. JRA. */ if (!lp_widelinks(snum)) { 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, server_id_self(), smbd_messaging_context(), smbd_event_context(), conn); } /* ROOT Activities: */ /* * 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); *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; } /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "root preexec = " line */ if (*lp_rootpreexec(snum)) { 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); *pstatus = NT_STATUS_ACCESS_DENIED; return NULL; } } /* USER Activites: */ if (!change_to_user(conn, conn->vuid)) { /* No point continuing if they fail the basic checks */ DEBUG(0,("Can't become connected user!\n")); yield_connection(conn, lp_servicename(snum)); conn_free(conn); *pstatus = NT_STATUS_LOGON_FAILURE; return NULL; } /* Remember that a different vuid can connect later without these * checks... */ /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "preexec = " line */ if (*lp_preexec(snum)) { 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)); *pstatus = NT_STATUS_ACCESS_DENIED; goto err_root_exit; } } #ifdef WITH_FAKE_KASERVER if (lp_afs_share(snum)) { afs_login(conn); } #endif /* Add veto/hide lists */ if (!IS_IPC(conn) && !IS_PRINT(conn)) { 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), conn->server_info->unix_name) < 0) { DEBUG(0,("make_connection: VFS make connection failed!\n")); *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 during the tree connect, instead relying on permission 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, 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))); } else { DEBUG(0,("'%s' does not exist or permission denied " "when connecting to [%s] Error was %s\n", conn->connectpath, lp_servicename(snum), strerror(errno) )); } *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) { 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 * (at least initially). */ if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address ); dbgtext( "%s", srv_is_signing_active(smbd_server_conn) ? "signed " : ""); dbgtext( "connect to service %s ", lp_servicename(snum) ); 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); 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); } yield_connection(conn, lp_servicename(snum)); conn_free(conn); return NULL; }
static NTSTATUS make_connection_snum(struct smbXsrv_connection *xconn, connection_struct *conn, int snum, struct user_struct *vuser, const char *pdev) { struct smbd_server_connection *sconn = xconn->client->sconn; struct smb_filename *smb_fname_cpath = NULL; fstring dev; int ret; bool on_err_call_dis_hook = false; uid_t effuid; gid_t effgid; NTSTATUS status; fstrcpy(dev, pdev); status = share_sanity_checks(sconn->remote_address, sconn->remote_hostname, snum, dev); if (NT_STATUS_IS_ERR(status)) { goto err_root_exit; } conn->params->service = snum; status = create_connection_session_info(sconn, conn, snum, vuser->session_info, &conn->session_info); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("create_connection_session_info failed: %s\n", nt_errstr(status))); goto err_root_exit; } if (lp_guest_only(snum)) { conn->force_user = true; } conn->num_files_open = 0; conn->lastused = conn->lastused_count = time(NULL); conn->printer = (strncmp(dev,"LPT",3) == 0); conn->ipc = ( (strncmp(dev,"IPC",3) == 0) || ( lp_enable_asu_support() && strequal(dev,"ADMIN$")) ); /* Case options for the share. */ if (lp_case_sensitive(snum) == Auto) { /* We will be setting this per packet. Set to be case * insensitive for now. */ conn->case_sensitive = False; } else { conn->case_sensitive = (bool)lp_case_sensitive(snum); } conn->case_preserve = lp_preserve_case(snum); conn->short_case_preserve = lp_short_preserve_case(snum); conn->encrypt_level = lp_smb_encrypt(snum); conn->veto_list = NULL; conn->hide_list = NULL; conn->veto_oplock_list = NULL; conn->aio_write_behind_list = NULL; conn->read_only = lp_read_only(SNUM(conn)); status = set_conn_force_user_group(conn, snum); if (!NT_STATUS_IS_OK(status)) { goto err_root_exit; } conn->vuid = vuser->vuid; { char *s = talloc_sub_advanced(talloc_tos(), lp_servicename(talloc_tos(), SNUM(conn)), conn->session_info->unix_info->unix_name, conn->connectpath, conn->session_info->unix_token->gid, conn->session_info->unix_info->sanitized_username, conn->session_info->info->domain_name, lp_path(talloc_tos(), snum)); if (!s) { status = NT_STATUS_NO_MEMORY; goto err_root_exit; } if (!set_conn_connectpath(conn,s)) { TALLOC_FREE(s); status = NT_STATUS_NO_MEMORY; goto err_root_exit; } DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(talloc_tos(), snum))); TALLOC_FREE(s); } /* * Set up the share security descriptor. * NOTE - we use the *INCOMING USER* session_info * here, as does (indirectly) change_to_user(), * which can be called on any incoming packet. * This way we set up the share access based * on the authenticated user, not the forced * user. See bug: * * https://bugzilla.samba.org/show_bug.cgi?id=9878 */ status = check_user_share_access(conn, vuser->session_info, &conn->share_access, &conn->read_only); if (!NT_STATUS_IS_OK(status)) { goto err_root_exit; } /* Initialise VFS function pointers */ if (!smbd_vfs_init(conn)) { DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(talloc_tos(), snum))); status = NT_STATUS_BAD_NETWORK_NAME; goto err_root_exit; } /* ROOT Activities: */ /* explicitly check widelinks here so that we can correctly warn * in the logs. */ widelinks_warning(snum); /* * Enforce the max connections parameter. */ if ((lp_max_connections(snum) > 0) && (count_current_connections(lp_servicename(talloc_tos(), SNUM(conn)), True) >= lp_max_connections(snum))) { DEBUG(1, ("Max connections (%d) exceeded for %s\n", lp_max_connections(snum), lp_servicename(talloc_tos(), snum))); status = NT_STATUS_INSUFFICIENT_RESOURCES; goto err_root_exit; } /* Invoke VFS make connection hook - this must be the first filesystem operation that we do. */ if (SMB_VFS_CONNECT(conn, lp_servicename(talloc_tos(), snum), conn->session_info->unix_info->unix_name) < 0) { DBG_WARNING("SMB_VFS_CONNECT for service '%s' at '%s' failed: %s\n", lp_servicename(talloc_tos(), snum), conn->connectpath, strerror(errno)); status = NT_STATUS_UNSUCCESSFUL; goto err_root_exit; } /* Any error exit after here needs to call the disconnect hook. */ on_err_call_dis_hook = true; if ((!conn->printer) && (!conn->ipc) && lp_change_notify()) { if (sconn->notify_ctx == NULL) { sconn->notify_ctx = notify_init( sconn, sconn->msg_ctx, sconn->ev_ctx); status = messaging_register( sconn->msg_ctx, sconn, MSG_SMB_NOTIFY_CANCEL_DELETED, smbd_notify_cancel_deleted); } if (sconn->sys_notify_ctx == NULL) { sconn->sys_notify_ctx = sys_notify_context_create( sconn, sconn->ev_ctx); } } if (lp_kernel_oplocks(snum)) { init_kernel_oplocks(conn->sconn); } /* * Fix compatibility issue pointed out by Volker. * We pass the conn->connectpath to the preexec * scripts as a parameter, so attempt to canonicalize * it here before calling the preexec scripts. * We ignore errors here, as it is possible that * the conn->connectpath doesn't exist yet and * the preexec scripts will create them. */ (void)canonicalize_connect_path(conn); /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "root preexec = " line */ if (*lp_root_preexec(talloc_tos(), snum)) { char *cmd = talloc_sub_advanced(talloc_tos(), lp_servicename(talloc_tos(), SNUM(conn)), conn->session_info->unix_info->unix_name, conn->connectpath, conn->session_info->unix_token->gid, conn->session_info->unix_info->sanitized_username, conn->session_info->info->domain_name, lp_root_preexec(talloc_tos(), snum)); DEBUG(5,("cmd=%s\n",cmd)); ret = smbrun(cmd,NULL); TALLOC_FREE(cmd); if (ret != 0 && lp_root_preexec_close(snum)) { DEBUG(1,("root preexec gave %d - failing " "connection\n", ret)); status = NT_STATUS_ACCESS_DENIED; goto err_root_exit; } } /* USER Activites: */ if (!change_to_user(conn, conn->vuid)) { /* No point continuing if they fail the basic checks */ DEBUG(0,("Can't become connected user!\n")); status = NT_STATUS_LOGON_FAILURE; goto err_root_exit; } effuid = geteuid(); effgid = getegid(); /* Remember that a different vuid can connect later without these * checks... */ /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "preexec = " line */ if (*lp_preexec(talloc_tos(), snum)) { char *cmd = talloc_sub_advanced(talloc_tos(), lp_servicename(talloc_tos(), SNUM(conn)), conn->session_info->unix_info->unix_name, conn->connectpath, conn->session_info->unix_token->gid, conn->session_info->unix_info->sanitized_username, conn->session_info->info->domain_name, lp_preexec(talloc_tos(), snum)); ret = smbrun(cmd,NULL); TALLOC_FREE(cmd); if (ret != 0 && lp_preexec_close(snum)) { DEBUG(1,("preexec gave %d - failing connection\n", ret)); status = NT_STATUS_ACCESS_DENIED; goto err_root_exit; } } #ifdef WITH_FAKE_KASERVER if (lp_afs_share(snum)) { afs_login(conn); } #endif /* * we've finished with the user stuff - go back to root * so the SMB_VFS_STAT call will only fail on path errors, * not permission problems. */ change_to_root_user(); /* ROOT Activites: */ /* * If widelinks are disallowed we need to canonicalise the connect * path here to ensure we don't have any symlinks in the * connectpath. We will be checking all paths on this connection are * below this directory. We must do this after the VFS init as we * depend on the realpath() pointer in the vfs table. JRA. */ if (!lp_widelinks(snum)) { if (!canonicalize_connect_path(conn)) { DEBUG(0, ("canonicalize_connect_path failed " "for service %s, path %s\n", lp_servicename(talloc_tos(), snum), conn->connectpath)); status = NT_STATUS_BAD_NETWORK_NAME; goto err_root_exit; } } /* Add veto/hide lists */ if (!IS_IPC(conn) && !IS_PRINT(conn)) { set_namearray( &conn->veto_list, lp_veto_files(talloc_tos(), snum)); set_namearray( &conn->hide_list, lp_hide_files(talloc_tos(), snum)); set_namearray( &conn->veto_oplock_list, lp_veto_oplock_files(talloc_tos(), snum)); set_namearray( &conn->aio_write_behind_list, lp_aio_write_behind(talloc_tos(), snum)); } smb_fname_cpath = synthetic_smb_fname(talloc_tos(), conn->connectpath, NULL, NULL); if (smb_fname_cpath == NULL) { status = NT_STATUS_NO_MEMORY; goto err_root_exit; } /* win2000 does not check the permissions on the directory during the tree connect, instead relying on permission 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, 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(talloc_tos(), snum))); } else { DEBUG(0,("'%s' does not exist or permission denied " "when connecting to [%s] Error was %s\n", conn->connectpath, lp_servicename(talloc_tos(), snum), strerror(errno) )); } status = NT_STATUS_BAD_NETWORK_NAME; goto err_root_exit; } conn->base_share_dev = smb_fname_cpath->st.st_ex_dev; talloc_free(conn->origpath); conn->origpath = talloc_strdup(conn, conn->connectpath); /* 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 * (at least initially). */ if( DEBUGLVL( IS_IPC(conn) ? 3 : 2 ) ) { dbgtext( "%s (%s) ", get_remote_machine_name(), tsocket_address_string(conn->sconn->remote_address, talloc_tos()) ); dbgtext( "%s", srv_is_signing_active(xconn) ? "signed " : ""); dbgtext( "connect to service %s ", lp_servicename(talloc_tos(), snum) ); dbgtext( "initially as user %s ", conn->session_info->unix_info->unix_name ); dbgtext( "(uid=%d, gid=%d) ", (int)effuid, (int)effgid ); dbgtext( "(pid %d)\n", (int)getpid() ); } return status; err_root_exit: TALLOC_FREE(smb_fname_cpath); /* We must exit this function as root. */ if (geteuid() != 0) { change_to_root_user(); } if (on_err_call_dis_hook) { /* Call VFS disconnect hook */ SMB_VFS_DISCONNECT(conn); } return status; }
static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session, struct smbd_smb2_request *smb2req, uint8_t in_security_mode, struct auth_session_info *session_info, uint16_t *out_session_flags, uint64_t *out_session_id) { NTSTATUS status; bool guest = false; uint8_t session_key[16]; struct smbXsrv_session *x = session; struct smbXsrv_connection *xconn = smb2req->xconn; struct _derivation { DATA_BLOB label; DATA_BLOB context; }; struct { struct _derivation signing; struct _derivation encryption; struct _derivation decryption; struct _derivation application; } derivation = { }; if (xconn->protocol >= PROTOCOL_SMB3_10) { struct smbXsrv_preauth *preauth; struct _derivation *d; DATA_BLOB p; struct hc_sha512state sctx; size_t i; preauth = talloc_move(smb2req, &session->preauth); samba_SHA512_Init(&sctx); samba_SHA512_Update(&sctx, preauth->sha512_value, sizeof(preauth->sha512_value)); for (i = 1; i < smb2req->in.vector_count; i++) { samba_SHA512_Update(&sctx, smb2req->in.vector[i].iov_base, smb2req->in.vector[i].iov_len); } samba_SHA512_Final(preauth->sha512_value, &sctx); p = data_blob_const(preauth->sha512_value, sizeof(preauth->sha512_value)); d = &derivation.signing; d->label = data_blob_string_const_null("SMBSigningKey"); d->context = p; d = &derivation.decryption; d->label = data_blob_string_const_null("SMBC2SCipherKey"); d->context = p; d = &derivation.encryption; d->label = data_blob_string_const_null("SMBS2CCipherKey"); d->context = p; d = &derivation.application; d->label = data_blob_string_const_null("SMBAppKey"); d->context = p; } else if (xconn->protocol >= PROTOCOL_SMB2_24) { struct _derivation *d; d = &derivation.signing; d->label = data_blob_string_const_null("SMB2AESCMAC"); d->context = data_blob_string_const_null("SmbSign"); d = &derivation.decryption; d->label = data_blob_string_const_null("SMB2AESCCM"); d->context = data_blob_string_const_null("ServerIn "); d = &derivation.encryption; d->label = data_blob_string_const_null("SMB2AESCCM"); d->context = data_blob_string_const_null("ServerOut"); d = &derivation.application; d->label = data_blob_string_const_null("SMB2APP"); d->context = data_blob_string_const_null("SmbRpc"); } if ((in_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) || (xconn->smb2.server.security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED)) { x->global->signing_required = true; } if ((lp_smb_encrypt(-1) >= SMB_SIGNING_DESIRED) && (xconn->smb2.client.capabilities & SMB2_CAP_ENCRYPTION)) { x->encryption_desired = true; } if (lp_smb_encrypt(-1) == SMB_SIGNING_REQUIRED) { x->encryption_desired = true; x->global->encryption_required = true; } if (security_session_user_level(session_info, NULL) < SECURITY_USER) { /* we map anonymous to guest internally */ *out_session_flags |= SMB2_SESSION_FLAG_IS_GUEST; *out_session_flags |= SMB2_SESSION_FLAG_IS_NULL; /* force no signing */ x->global->signing_required = false; guest = true; } if (guest && x->global->encryption_required) { DEBUG(1,("reject guest session as encryption is required\n")); return NT_STATUS_ACCESS_DENIED; } if (xconn->smb2.server.cipher == 0) { if (x->global->encryption_required) { DEBUG(1,("reject session with dialect[0x%04X] " "as encryption is required\n", xconn->smb2.server.dialect)); return NT_STATUS_ACCESS_DENIED; } } if (x->encryption_desired) { *out_session_flags |= SMB2_SESSION_FLAG_ENCRYPT_DATA; } ZERO_STRUCT(session_key); memcpy(session_key, session_info->session_key.data, MIN(session_info->session_key.length, sizeof(session_key))); x->global->signing_key = data_blob_talloc(x->global, session_key, sizeof(session_key)); if (x->global->signing_key.data == NULL) { ZERO_STRUCT(session_key); return NT_STATUS_NO_MEMORY; } if (xconn->protocol >= PROTOCOL_SMB2_24) { struct _derivation *d = &derivation.signing; smb2_key_derivation(session_key, sizeof(session_key), d->label.data, d->label.length, d->context.data, d->context.length, x->global->signing_key.data); } if (xconn->protocol >= PROTOCOL_SMB2_24) { struct _derivation *d = &derivation.decryption; x->global->decryption_key = data_blob_talloc(x->global, session_key, sizeof(session_key)); if (x->global->decryption_key.data == NULL) { ZERO_STRUCT(session_key); return NT_STATUS_NO_MEMORY; } smb2_key_derivation(session_key, sizeof(session_key), d->label.data, d->label.length, d->context.data, d->context.length, x->global->decryption_key.data); } if (xconn->protocol >= PROTOCOL_SMB2_24) { struct _derivation *d = &derivation.encryption; size_t nonce_size; x->global->encryption_key = data_blob_talloc(x->global, session_key, sizeof(session_key)); if (x->global->encryption_key.data == NULL) { ZERO_STRUCT(session_key); return NT_STATUS_NO_MEMORY; } smb2_key_derivation(session_key, sizeof(session_key), d->label.data, d->label.length, d->context.data, d->context.length, x->global->encryption_key.data); /* * CCM and GCM algorithms must never have their * nonce wrap, or the security of the whole * communication and the keys is destroyed. * We must drop the connection once we have * transfered too much data. * * NOTE: We assume nonces greater than 8 bytes. */ generate_random_buffer((uint8_t *)&x->nonce_high_random, sizeof(x->nonce_high_random)); switch (xconn->smb2.server.cipher) { case SMB2_ENCRYPTION_AES128_CCM: nonce_size = AES_CCM_128_NONCE_SIZE; break; case SMB2_ENCRYPTION_AES128_GCM: nonce_size = AES_GCM_128_IV_SIZE; break; default: nonce_size = 0; break; } x->nonce_high_max = SMB2_NONCE_HIGH_MAX(nonce_size); x->nonce_high = 0; x->nonce_low = 0; } x->global->application_key = data_blob_dup_talloc(x->global, x->global->signing_key); if (x->global->application_key.data == NULL) { ZERO_STRUCT(session_key); return NT_STATUS_NO_MEMORY; } if (xconn->protocol >= PROTOCOL_SMB2_24) { struct _derivation *d = &derivation.application; smb2_key_derivation(session_key, sizeof(session_key), d->label.data, d->label.length, d->context.data, d->context.length, x->global->application_key.data); } ZERO_STRUCT(session_key); x->global->channels[0].signing_key = data_blob_dup_talloc(x->global->channels, x->global->signing_key); if (x->global->channels[0].signing_key.data == NULL) { return NT_STATUS_NO_MEMORY; } data_blob_clear_free(&session_info->session_key); session_info->session_key = data_blob_dup_talloc(session_info, x->global->application_key); if (session_info->session_key.data == NULL) { return NT_STATUS_NO_MEMORY; } session->compat = talloc_zero(session, struct user_struct); if (session->compat == NULL) { return NT_STATUS_NO_MEMORY; } session->compat->session = session; session->compat->homes_snum = -1; session->compat->session_info = session_info; session->compat->session_keystr = NULL; session->compat->vuid = session->global->session_wire_id; DLIST_ADD(smb2req->sconn->users, session->compat); smb2req->sconn->num_users++; if (security_session_user_level(session_info, NULL) >= SECURITY_USER) { session->compat->homes_snum = register_homes_share(session_info->unix_info->unix_name); } set_current_user_info(session_info->unix_info->sanitized_username, session_info->unix_info->unix_name, session_info->info->domain_name); reload_services(smb2req->sconn, conn_snum_used, true); session->status = NT_STATUS_OK; session->global->auth_session_info = session_info; session->global->auth_session_info_seqnum += 1; session->global->channels[0].auth_session_info_seqnum = session->global->auth_session_info_seqnum; session->global->auth_time = timeval_to_nttime(&smb2req->request_time); session->global->expiration_time = gensec_expire_time(session->gensec); if (!session_claim(session)) { DEBUG(1, ("smb2: Failed to claim session " "for vuid=%llu\n", (unsigned long long)session->compat->vuid)); return NT_STATUS_LOGON_FAILURE; } status = smbXsrv_session_update(session); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("smb2: Failed to update session for vuid=%llu - %s\n", (unsigned long long)session->compat->vuid, nt_errstr(status))); return NT_STATUS_LOGON_FAILURE; } /* * we attach the session to the request * so that the response can be signed */ if (!guest) { smb2req->do_signing = true; } global_client_caps |= (CAP_LEVEL_II_OPLOCKS|CAP_STATUS32); *out_session_id = session->global->session_wire_id; return NT_STATUS_OK; }
NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req) { struct smbXsrv_connection *xconn = req->xconn; NTSTATUS status; const uint8_t *inbody; const uint8_t *indyn = NULL; DATA_BLOB outbody; DATA_BLOB outdyn; DATA_BLOB negprot_spnego_blob; uint16_t security_offset; DATA_BLOB security_buffer; size_t expected_dyn_size = 0; size_t c; uint16_t security_mode; uint16_t dialect_count; uint16_t in_security_mode; uint32_t in_capabilities; DATA_BLOB in_guid_blob; struct GUID in_guid; struct smb2_negotiate_contexts in_c = { .num_contexts = 0, }; struct smb2_negotiate_context *in_preauth = NULL; struct smb2_negotiate_context *in_cipher = NULL; struct smb2_negotiate_contexts out_c = { .num_contexts = 0, }; DATA_BLOB out_negotiate_context_blob = data_blob_null; uint32_t out_negotiate_context_offset = 0; uint16_t out_negotiate_context_count = 0; uint16_t dialect = 0; uint32_t capabilities; DATA_BLOB out_guid_blob; struct GUID out_guid; enum protocol_types protocol = PROTOCOL_NONE; uint32_t max_limit; uint32_t max_trans = lp_smb2_max_trans(); uint32_t max_read = lp_smb2_max_read(); uint32_t max_write = lp_smb2_max_write(); NTTIME now = timeval_to_nttime(&req->request_time); bool signing_required = true; bool ok; status = smbd_smb2_request_verify_sizes(req, 0x24); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); dialect_count = SVAL(inbody, 0x02); in_security_mode = SVAL(inbody, 0x04); in_capabilities = IVAL(inbody, 0x08); in_guid_blob = data_blob_const(inbody + 0x0C, 16); if (dialect_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } status = GUID_from_ndr_blob(&in_guid_blob, &in_guid); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } expected_dyn_size = dialect_count * 2; if (SMBD_SMB2_IN_DYN_LEN(req) < expected_dyn_size) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } indyn = SMBD_SMB2_IN_DYN_PTR(req); protocol = smbd_smb2_protocol_dialect_match(indyn, dialect_count, &dialect); for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_server_max_protocol() < PROTOCOL_SMB2_10) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_2FF) { if (xconn->smb2.allow_2ff) { xconn->smb2.allow_2ff = false; protocol = PROTOCOL_SMB2_10; break; } } } if (protocol == PROTOCOL_NONE) { return smbd_smb2_request_error(req, NT_STATUS_NOT_SUPPORTED); } if (protocol >= PROTOCOL_SMB3_10) { uint32_t in_negotiate_context_offset = 0; uint16_t in_negotiate_context_count = 0; DATA_BLOB in_negotiate_context_blob = data_blob_null; size_t ofs; in_negotiate_context_offset = IVAL(inbody, 0x1C); in_negotiate_context_count = SVAL(inbody, 0x20); ofs = SMB2_HDR_BODY; ofs += SMBD_SMB2_IN_BODY_LEN(req); ofs += expected_dyn_size; if ((ofs % 8) != 0) { ofs += 8 - (ofs % 8); } if (in_negotiate_context_offset != ofs) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } ofs -= SMB2_HDR_BODY; ofs -= SMBD_SMB2_IN_BODY_LEN(req); if (SMBD_SMB2_IN_DYN_LEN(req) < ofs) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } in_negotiate_context_blob = data_blob_const(indyn, SMBD_SMB2_IN_DYN_LEN(req)); in_negotiate_context_blob.data += ofs; in_negotiate_context_blob.length -= ofs; status = smb2_negotiate_context_parse(req, in_negotiate_context_blob, &in_c); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } if (in_negotiate_context_count != in_c.num_contexts) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } } if ((dialect != SMB2_DIALECT_REVISION_2FF) && (protocol >= PROTOCOL_SMB2_10) && !GUID_all_zero(&in_guid)) { ok = remote_arch_cache_update(&in_guid); if (!ok) { return smbd_smb2_request_error( req, NT_STATUS_UNSUCCESSFUL); } } switch (get_remote_arch()) { case RA_VISTA: case RA_SAMBA: case RA_CIFSFS: case RA_OSX: break; default: set_remote_arch(RA_VISTA); break; } fstr_sprintf(remote_proto, "SMB%X_%02X", (dialect >> 8) & 0xFF, dialect & 0xFF); reload_services(req->sconn, conn_snum_used, true); DEBUG(3,("Selected protocol %s\n", remote_proto)); in_preauth = smb2_negotiate_context_find(&in_c, SMB2_PREAUTH_INTEGRITY_CAPABILITIES); if (protocol >= PROTOCOL_SMB3_10 && in_preauth == NULL) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } in_cipher = smb2_negotiate_context_find(&in_c, SMB2_ENCRYPTION_CAPABILITIES); /* negprot_spnego() returns a the server guid in the first 16 bytes */ negprot_spnego_blob = negprot_spnego(req, xconn); if (negprot_spnego_blob.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } if (negprot_spnego_blob.length < 16) { return smbd_smb2_request_error(req, NT_STATUS_INTERNAL_ERROR); } security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; /* * We use xconn->smb1.signing_state as that's already present * and used lpcfg_server_signing_allowed() to get the correct * defaults, e.g. signing_required for an ad_dc. */ signing_required = smb_signing_is_mandatory(xconn->smb1.signing_state); if (signing_required) { security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED; } capabilities = 0; if (lp_host_msdfs()) { capabilities |= SMB2_CAP_DFS; } if (protocol >= PROTOCOL_SMB2_10 && lp_smb2_leases() && lp_oplocks(GLOBAL_SECTION_SNUM) && !lp_kernel_oplocks(GLOBAL_SECTION_SNUM)) { capabilities |= SMB2_CAP_LEASING; } if ((protocol >= PROTOCOL_SMB2_24) && (lp_smb_encrypt(-1) != SMB_SIGNING_OFF) && (in_capabilities & SMB2_CAP_ENCRYPTION)) { capabilities |= SMB2_CAP_ENCRYPTION; } /* * 0x10000 (65536) is the maximum allowed message size * for SMB 2.0 */ max_limit = 0x10000; if (protocol >= PROTOCOL_SMB2_10) { int p = 0; if (tsocket_address_is_inet(req->sconn->local_address, "ip")) { p = tsocket_address_inet_port(req->sconn->local_address); } /* largeMTU is not supported over NBT (tcp port 139) */ if (p != NBT_SMB_PORT) { capabilities |= SMB2_CAP_LARGE_MTU; xconn->smb2.credits.multicredit = true; /* * We allow up to almost 16MB. * * The maximum PDU size is 0xFFFFFF (16776960) * and we need some space for the header. */ max_limit = 0xFFFF00; } } /* * the defaults are 8MB, but we'll limit this to max_limit based on * the dialect (64kb for SMB 2.0, 8MB for SMB >= 2.1 with LargeMTU) * * user configured values exceeding the limits will be overwritten, * only smaller values will be accepted */ max_trans = MIN(max_limit, lp_smb2_max_trans()); max_read = MIN(max_limit, lp_smb2_max_read()); max_write = MIN(max_limit, lp_smb2_max_write()); if (in_preauth != NULL) { size_t needed = 4; uint16_t hash_count; uint16_t salt_length; uint16_t selected_preauth = 0; const uint8_t *p; uint8_t buf[38]; DATA_BLOB b; size_t i; if (in_preauth->data.length < needed) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } hash_count = SVAL(in_preauth->data.data, 0); salt_length = SVAL(in_preauth->data.data, 2); if (hash_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } p = in_preauth->data.data + needed; needed += hash_count * 2; needed += salt_length; if (in_preauth->data.length < needed) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } for (i=0; i < hash_count; i++) { uint16_t v; v = SVAL(p, 0); p += 2; if (v == SMB2_PREAUTH_INTEGRITY_SHA512) { selected_preauth = v; break; } } if (selected_preauth == 0) { return smbd_smb2_request_error(req, NT_STATUS_SMB_NO_PREAUTH_INTEGRITY_HASH_OVERLAP); } SSVAL(buf, 0, 1); /* HashAlgorithmCount */ SSVAL(buf, 2, 32); /* SaltLength */ SSVAL(buf, 4, selected_preauth); generate_random_buffer(buf + 6, 32); b = data_blob_const(buf, sizeof(buf)); status = smb2_negotiate_context_add(req, &out_c, SMB2_PREAUTH_INTEGRITY_CAPABILITIES, b); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } req->preauth = &req->xconn->smb2.preauth; } if (in_cipher != NULL) { size_t needed = 2; uint16_t cipher_count; const uint8_t *p; uint8_t buf[4]; DATA_BLOB b; size_t i; bool aes_128_ccm_supported = false; bool aes_128_gcm_supported = false; capabilities &= ~SMB2_CAP_ENCRYPTION; if (in_cipher->data.length < needed) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } cipher_count = SVAL(in_cipher->data.data, 0); if (cipher_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } p = in_cipher->data.data + needed; needed += cipher_count * 2; if (in_cipher->data.length < needed) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } for (i=0; i < cipher_count; i++) { uint16_t v; v = SVAL(p, 0); p += 2; if (v == SMB2_ENCRYPTION_AES128_GCM) { aes_128_gcm_supported = true; } if (v == SMB2_ENCRYPTION_AES128_CCM) { aes_128_ccm_supported = true; } } /* * For now we preferr CCM because our implementation * is faster than GCM, see bug #11451. */ if (aes_128_ccm_supported) { xconn->smb2.server.cipher = SMB2_ENCRYPTION_AES128_CCM; } else if (aes_128_gcm_supported) { xconn->smb2.server.cipher = SMB2_ENCRYPTION_AES128_GCM; } SSVAL(buf, 0, 1); /* ChiperCount */ SSVAL(buf, 2, xconn->smb2.server.cipher); b = data_blob_const(buf, sizeof(buf)); status = smb2_negotiate_context_add(req, &out_c, SMB2_ENCRYPTION_CAPABILITIES, b); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } } if (capabilities & SMB2_CAP_ENCRYPTION) { xconn->smb2.server.cipher = SMB2_ENCRYPTION_AES128_CCM; } if (protocol >= PROTOCOL_SMB2_22 && xconn->client->server_multi_channel_enabled) { if (in_capabilities & SMB2_CAP_MULTI_CHANNEL) { capabilities |= SMB2_CAP_MULTI_CHANNEL; } } security_offset = SMB2_HDR_BODY + 0x40; #if 1 /* Try SPNEGO auth... */ security_buffer = data_blob_const(negprot_spnego_blob.data + 16, negprot_spnego_blob.length - 16); #else /* for now we want raw NTLMSSP */ security_buffer = data_blob_const(NULL, 0); #endif if (out_c.num_contexts != 0) { status = smb2_negotiate_context_push(req, &out_negotiate_context_blob, out_c); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } } if (out_negotiate_context_blob.length != 0) { static const uint8_t zeros[8]; size_t pad = 0; size_t ofs; outdyn = data_blob_dup_talloc(req, security_buffer); if (outdyn.length != security_buffer.length) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } ofs = security_offset + security_buffer.length; if ((ofs % 8) != 0) { pad = 8 - (ofs % 8); } ofs += pad; ok = data_blob_append(req, &outdyn, zeros, pad); if (!ok) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } ok = data_blob_append(req, &outdyn, out_negotiate_context_blob.data, out_negotiate_context_blob.length); if (!ok) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } out_negotiate_context_offset = ofs; out_negotiate_context_count = out_c.num_contexts; } else { outdyn = security_buffer; } out_guid_blob = data_blob_const(negprot_spnego_blob.data, 16); status = GUID_from_ndr_blob(&out_guid_blob, &out_guid); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } outbody = smbd_smb2_generate_outbody(req, 0x40); if (outbody.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } SSVAL(outbody.data, 0x00, 0x40 + 1); /* struct size */ SSVAL(outbody.data, 0x02, security_mode); /* security mode */ SSVAL(outbody.data, 0x04, dialect); /* dialect revision */ SSVAL(outbody.data, 0x06, out_negotiate_context_count); /* reserved/NegotiateContextCount */ memcpy(outbody.data + 0x08, out_guid_blob.data, 16); /* server guid */ SIVAL(outbody.data, 0x18, capabilities); /* capabilities */ SIVAL(outbody.data, 0x1C, max_trans); /* max transact size */ SIVAL(outbody.data, 0x20, max_read); /* max read size */ SIVAL(outbody.data, 0x24, max_write); /* max write size */ SBVAL(outbody.data, 0x28, now); /* system time */ SBVAL(outbody.data, 0x30, 0); /* server start time */ SSVAL(outbody.data, 0x38, security_offset); /* security buffer offset */ SSVAL(outbody.data, 0x3A, security_buffer.length); /* security buffer length */ SIVAL(outbody.data, 0x3C, out_negotiate_context_offset); /* reserved/NegotiateContextOffset */ req->sconn->using_smb2 = true; if (dialect != SMB2_DIALECT_REVISION_2FF) { struct smbXsrv_client_global0 *global0 = NULL; status = smbXsrv_connection_init_tables(xconn, protocol); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } xconn->smb2.client.capabilities = in_capabilities; xconn->smb2.client.security_mode = in_security_mode; xconn->smb2.client.guid = in_guid; xconn->smb2.client.num_dialects = dialect_count; xconn->smb2.client.dialects = talloc_array(xconn, uint16_t, dialect_count); if (xconn->smb2.client.dialects == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } for (c=0; c < dialect_count; c++) { xconn->smb2.client.dialects[c] = SVAL(indyn, c*2); } xconn->smb2.server.capabilities = capabilities; xconn->smb2.server.security_mode = security_mode; xconn->smb2.server.guid = out_guid; xconn->smb2.server.dialect = dialect; xconn->smb2.server.max_trans = max_trans; xconn->smb2.server.max_read = max_read; xconn->smb2.server.max_write = max_write; if (xconn->protocol < PROTOCOL_SMB2_10) { /* * SMB2_02 doesn't support client guids */ return smbd_smb2_request_done(req, outbody, &outdyn); } if (!xconn->client->server_multi_channel_enabled) { /* * Only deal with the client guid database * if multi-channel is enabled. */ return smbd_smb2_request_done(req, outbody, &outdyn); } if (xconn->smb2.client.guid_verified) { /* * The connection was passed from another * smbd process. */ return smbd_smb2_request_done(req, outbody, &outdyn); } status = smb2srv_client_lookup_global(xconn->client, xconn->smb2.client.guid, req, &global0); /* * TODO: check for races... */ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECTID_NOT_FOUND)) { /* * This stores the new client information in * smbXsrv_client_global.tdb */ xconn->client->global->client_guid = xconn->smb2.client.guid; status = smbXsrv_client_update(xconn->client); if (!NT_STATUS_IS_OK(status)) { return status; } xconn->smb2.client.guid_verified = true; } else if (NT_STATUS_IS_OK(status)) { status = smb2srv_client_connection_pass(req, global0); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } smbd_server_connection_terminate(xconn, "passed connection"); return NT_STATUS_OBJECTID_EXISTS; } else { return smbd_smb2_request_error(req, status); } } return smbd_smb2_request_done(req, outbody, &outdyn); }
NTSTATUS smbd_smb2_request_process_negprot(struct smbd_smb2_request *req) { NTSTATUS status; const uint8_t *inbody; const uint8_t *indyn = NULL; DATA_BLOB outbody; DATA_BLOB outdyn; DATA_BLOB negprot_spnego_blob; uint16_t security_offset; DATA_BLOB security_buffer; size_t expected_dyn_size = 0; size_t c; uint16_t security_mode; uint16_t dialect_count; uint16_t in_security_mode; uint32_t in_capabilities; DATA_BLOB in_guid_blob; struct GUID in_guid; uint16_t dialect = 0; uint32_t capabilities; DATA_BLOB out_guid_blob; struct GUID out_guid; enum protocol_types protocol = PROTOCOL_NONE; uint32_t max_limit; uint32_t max_trans = lp_smb2_max_trans(); uint32_t max_read = lp_smb2_max_read(); uint32_t max_write = lp_smb2_max_write(); NTTIME now = timeval_to_nttime(&req->request_time); status = smbd_smb2_request_verify_sizes(req, 0x24); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } inbody = SMBD_SMB2_IN_BODY_PTR(req); dialect_count = SVAL(inbody, 0x02); in_security_mode = SVAL(inbody, 0x04); in_capabilities = IVAL(inbody, 0x08); in_guid_blob = data_blob_const(inbody + 0x0C, 16); if (dialect_count == 0) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } status = GUID_from_ndr_blob(&in_guid_blob, &in_guid); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } expected_dyn_size = dialect_count * 2; if (SMBD_SMB2_IN_DYN_LEN(req) < expected_dyn_size) { return smbd_smb2_request_error(req, NT_STATUS_INVALID_PARAMETER); } indyn = SMBD_SMB2_IN_DYN_PTR(req); for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB3_00) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB3_00) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB3_DIALECT_REVISION_300) { protocol = PROTOCOL_SMB3_00; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_24) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_24) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_224) { protocol = PROTOCOL_SMB2_24; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_22) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_22) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_222) { protocol = PROTOCOL_SMB2_22; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_10) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_10) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_210) { protocol = PROTOCOL_SMB2_10; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_02) { break; } if (lp_srv_minprotocol() > PROTOCOL_SMB2_02) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_202) { protocol = PROTOCOL_SMB2_02; break; } } for (c=0; protocol == PROTOCOL_NONE && c < dialect_count; c++) { if (lp_srv_maxprotocol() < PROTOCOL_SMB2_10) { break; } dialect = SVAL(indyn, c*2); if (dialect == SMB2_DIALECT_REVISION_2FF) { if (req->sconn->smb2.negprot_2ff) { req->sconn->smb2.negprot_2ff = false; protocol = PROTOCOL_SMB2_10; break; } } } if (protocol == PROTOCOL_NONE) { return smbd_smb2_request_error(req, NT_STATUS_NOT_SUPPORTED); } if (get_remote_arch() != RA_SAMBA) { set_remote_arch(RA_VISTA); } fstr_sprintf(remote_proto, "SMB%X_%02X", (dialect >> 8) & 0xFF, dialect & 0xFF); reload_services(req->sconn, conn_snum_used, true); DEBUG(3,("Selected protocol %s\n", remote_proto)); /* negprot_spnego() returns a the server guid in the first 16 bytes */ negprot_spnego_blob = negprot_spnego(req, req->sconn); if (negprot_spnego_blob.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } if (negprot_spnego_blob.length < 16) { return smbd_smb2_request_error(req, NT_STATUS_INTERNAL_ERROR); } security_mode = SMB2_NEGOTIATE_SIGNING_ENABLED; if (lp_server_signing() == SMB_SIGNING_REQUIRED) { security_mode |= SMB2_NEGOTIATE_SIGNING_REQUIRED; } capabilities = 0; if (lp_host_msdfs()) { capabilities |= SMB2_CAP_DFS; } if ((protocol >= PROTOCOL_SMB2_24) && (lp_smb_encrypt(-1) != SMB_SIGNING_OFF)) { if (in_capabilities & SMB2_CAP_ENCRYPTION) { capabilities |= SMB2_CAP_ENCRYPTION; } } /* * 0x10000 (65536) is the maximum allowed message size * for SMB 2.0 */ max_limit = 0x10000; if (protocol >= PROTOCOL_SMB2_10) { int p = 0; if (tsocket_address_is_inet(req->sconn->local_address, "ip")) { p = tsocket_address_inet_port(req->sconn->local_address); } /* largeMTU is not supported over NBT (tcp port 139) */ if (p != NBT_SMB_PORT) { capabilities |= SMB2_CAP_LARGE_MTU; req->sconn->smb2.supports_multicredit = true; /* SMB >= 2.1 has 1 MB of allowed size */ max_limit = 0x100000; /* 1MB */ } } /* * the defaults are 1MB, but we'll limit this to max_limit based on * the dialect (64kb for SMB2.0, 1MB for SMB2.1 with LargeMTU) * * user configured values exceeding the limits will be overwritten, * only smaller values will be accepted */ max_trans = MIN(max_limit, lp_smb2_max_trans()); max_read = MIN(max_limit, lp_smb2_max_read()); max_write = MIN(max_limit, lp_smb2_max_write()); security_offset = SMB2_HDR_BODY + 0x40; #if 1 /* Try SPNEGO auth... */ security_buffer = data_blob_const(negprot_spnego_blob.data + 16, negprot_spnego_blob.length - 16); #else /* for now we want raw NTLMSSP */ security_buffer = data_blob_const(NULL, 0); #endif out_guid_blob = data_blob_const(negprot_spnego_blob.data, 16); status = GUID_from_ndr_blob(&out_guid_blob, &out_guid); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } outbody = data_blob_talloc(req->out.vector, NULL, 0x40); if (outbody.data == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } SSVAL(outbody.data, 0x00, 0x40 + 1); /* struct size */ SSVAL(outbody.data, 0x02, security_mode); /* security mode */ SSVAL(outbody.data, 0x04, dialect); /* dialect revision */ SSVAL(outbody.data, 0x06, 0); /* reserved */ memcpy(outbody.data + 0x08, out_guid_blob.data, 16); /* server guid */ SIVAL(outbody.data, 0x18, capabilities); /* capabilities */ SIVAL(outbody.data, 0x1C, max_trans); /* max transact size */ SIVAL(outbody.data, 0x20, max_read); /* max read size */ SIVAL(outbody.data, 0x24, max_write); /* max write size */ SBVAL(outbody.data, 0x28, now); /* system time */ SBVAL(outbody.data, 0x30, 0); /* server start time */ SSVAL(outbody.data, 0x38, security_offset); /* security buffer offset */ SSVAL(outbody.data, 0x3A, security_buffer.length); /* security buffer length */ SIVAL(outbody.data, 0x3C, 0); /* reserved */ outdyn = security_buffer; req->sconn->using_smb2 = true; if (dialect != SMB2_DIALECT_REVISION_2FF) { struct smbXsrv_connection *conn = req->sconn->conn; status = smbXsrv_connection_init_tables(conn, protocol); if (!NT_STATUS_IS_OK(status)) { return smbd_smb2_request_error(req, status); } conn->smb2.client.capabilities = in_capabilities; conn->smb2.client.security_mode = in_security_mode; conn->smb2.client.guid = in_guid; conn->smb2.client.num_dialects = dialect_count; conn->smb2.client.dialects = talloc_array(conn, uint16_t, dialect_count); if (conn->smb2.client.dialects == NULL) { return smbd_smb2_request_error(req, NT_STATUS_NO_MEMORY); } for (c=0; c < dialect_count; c++) { conn->smb2.client.dialects[c] = SVAL(indyn, c*2); } conn->smb2.server.capabilities = capabilities; conn->smb2.server.security_mode = security_mode; conn->smb2.server.guid = out_guid; conn->smb2.server.dialect = dialect; conn->smb2.server.max_trans = max_trans; conn->smb2.server.max_read = max_read; conn->smb2.server.max_write = max_write; req->sconn->smb2.max_trans = max_trans; req->sconn->smb2.max_read = max_read; req->sconn->smb2.max_write = max_write; } return smbd_smb2_request_done(req, outbody, &outdyn); }
static NTSTATUS smbd_smb2_tree_connect(struct smbd_smb2_request *req, const char *in_path, uint8_t *out_share_type, uint32_t *out_share_flags, uint32_t *out_capabilities, uint32_t *out_maximal_access, uint32_t *out_tree_id) { struct smbXsrv_connection *conn = req->sconn->conn; const char *share = in_path; char *service = NULL; int snum = -1; struct smbXsrv_tcon *tcon; NTTIME now = timeval_to_nttime(&req->request_time); connection_struct *compat_conn = NULL; struct user_struct *compat_vuser = req->session->compat; NTSTATUS status; bool encryption_desired = req->session->encryption_desired; bool encryption_required = req->session->global->encryption_required; bool guest_session = false; if (strncmp(share, "\\\\", 2) == 0) { const char *p = strchr(share+2, '\\'); if (p) { share = p + 1; } } DEBUG(10,("smbd_smb2_tree_connect: path[%s] share[%s]\n", in_path, share)); service = talloc_strdup(talloc_tos(), share); if(!service) { return NT_STATUS_NO_MEMORY; } if (!strlower_m(service)) { DEBUG(2, ("strlower_m %s failed\n", service)); return NT_STATUS_INVALID_PARAMETER; } /* TODO: do more things... */ if (strequal(service,HOMES_NAME)) { if (compat_vuser->homes_snum == -1) { DEBUG(2, ("[homes] share not available for " "user %s because it was not found " "or created at session setup " "time\n", compat_vuser->session_info->unix_info->unix_name)); return NT_STATUS_BAD_NETWORK_NAME; } snum = compat_vuser->homes_snum; } else if ((compat_vuser->homes_snum != -1) && strequal(service, lp_servicename(talloc_tos(), compat_vuser->homes_snum))) { snum = compat_vuser->homes_snum; } else { snum = find_service(talloc_tos(), service, &service); if (!service) { return NT_STATUS_NO_MEMORY; } } if (snum < 0) { DEBUG(3,("smbd_smb2_tree_connect: couldn't find service %s\n", service)); return NT_STATUS_BAD_NETWORK_NAME; } if ((lp_smb_encrypt(snum) >= SMB_SIGNING_DESIRED) && (conn->smb2.client.capabilities & SMB2_CAP_ENCRYPTION)) { encryption_desired = true; } if (lp_smb_encrypt(snum) == SMB_SIGNING_REQUIRED) { encryption_desired = true; encryption_required = true; } if (security_session_user_level(compat_vuser->session_info, NULL) < SECURITY_USER) { guest_session = true; } if (guest_session && encryption_required) { DEBUG(1,("reject guest as encryption is required for service %s\n", service)); return NT_STATUS_ACCESS_DENIED; } if (!(conn->smb2.server.capabilities & SMB2_CAP_ENCRYPTION)) { if (encryption_required) { DEBUG(1,("reject tcon with dialect[0x%04X] " "as encryption is required for service %s\n", conn->smb2.server.dialect, service)); return NT_STATUS_ACCESS_DENIED; } } /* create a new tcon as child of the session */ status = smb2srv_tcon_create(req->session, now, &tcon); if (!NT_STATUS_IS_OK(status)) { return status; } tcon->encryption_desired = encryption_desired; tcon->global->encryption_required = encryption_required; compat_conn = make_connection_smb2(req->sconn, tcon, snum, req->session->compat, "???", &status); if (compat_conn == NULL) { TALLOC_FREE(tcon); return status; } tcon->global->share_name = lp_servicename(tcon->global, SNUM(compat_conn)); if (tcon->global->share_name == NULL) { conn_free(compat_conn); TALLOC_FREE(tcon); return NT_STATUS_NO_MEMORY; } tcon->global->session_global_id = req->session->global->session_global_id; tcon->compat = talloc_move(tcon, &compat_conn); tcon->status = NT_STATUS_OK; status = smbXsrv_tcon_update(tcon); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(tcon); return status; } if (IS_PRINT(tcon->compat)) { *out_share_type = SMB2_SHARE_TYPE_PRINT; } else if (IS_IPC(tcon->compat)) { *out_share_type = SMB2_SHARE_TYPE_PIPE; } else { *out_share_type = SMB2_SHARE_TYPE_DISK; } *out_share_flags = 0; if (lp_msdfs_root(SNUM(tcon->compat)) && lp_host_msdfs()) { *out_share_flags |= (SMB2_SHAREFLAG_DFS|SMB2_SHAREFLAG_DFS_ROOT); *out_capabilities = SMB2_SHARE_CAP_DFS; } else { *out_capabilities = 0; } switch(lp_csc_policy(SNUM(tcon->compat))) { case CSC_POLICY_MANUAL: break; case CSC_POLICY_DOCUMENTS: *out_share_flags |= SMB2_SHAREFLAG_AUTO_CACHING; break; case CSC_POLICY_PROGRAMS: *out_share_flags |= SMB2_SHAREFLAG_VDO_CACHING; break; case CSC_POLICY_DISABLE: *out_share_flags |= SMB2_SHAREFLAG_NO_CACHING; break; default: break; } if (lp_hideunreadable(SNUM(tcon->compat)) || lp_hideunwriteable_files(SNUM(tcon->compat))) { *out_share_flags |= SMB2_SHAREFLAG_ACCESS_BASED_DIRECTORY_ENUM; } if (encryption_desired) { *out_share_flags |= SMB2_SHAREFLAG_ENCRYPT_DATA; } *out_maximal_access = tcon->compat->share_access; *out_tree_id = tcon->global->tcon_wire_id; return NT_STATUS_OK; }
static connection_struct *make_connection_snum(int snum, user_struct *vuser, DATA_BLOB password, const char *pdev, NTSTATUS *status) { struct passwd *pass = NULL; bool guest = False; connection_struct *conn; SMB_STRUCT_STAT st; fstring user; fstring dev; int ret; char addr[INET6_ADDRSTRLEN]; bool on_err_call_dis_hook = false; *user = 0; fstrcpy(dev, pdev); SET_STAT_INVALID(st); if (NT_STATUS_IS_ERR(*status = share_sanity_checks(snum, dev))) { return NULL; } conn = conn_new(); if (!conn) { DEBUG(0,("Couldn't find free connection.\n")); *status = 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(talloc_tos(), 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_alloc(talloc_tos(), user); status2 = create_token_from_username(conn->mem_ctx, pass->pw_name, True, &conn->uid, &conn->gid, &found_username, &conn->nt_user_token); TALLOC_FREE(pass); 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")); conn_free(conn); *status = NT_STATUS_ACCESS_DENIED; return NULL; } add_session_user(user); 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) { /* We will be setting this per packet. Set to be case * insensitive for now. */ conn->case_sensitive = False; } else { 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; conn->aio_write_behind_list = NULL; string_set(&conn->dirpath,""); string_set(&conn->user,user); 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)) { conn_free(conn); *status = status2; return NULL; } string_set(&conn->user,user); conn->force_user = True; DEBUG(3,("Forced user %s\n",user)); } /* * 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)) { conn_free(conn); *status = status2; 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; } 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; for (i=0; i<conn->nt_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_dbg(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->user, conn->connectpath, conn->gid, get_current_username(), current_user_info.domain, lp_pathname(snum)); if (!s) { conn_free(conn); *status = NT_STATUS_NO_MEMORY; return NULL; } if (!set_conn_connectpath(conn,s)) { TALLOC_FREE(s); conn_free(conn); *status = NT_STATUS_NO_MEMORY; return NULL; } DEBUG(3,("Connect path is '%s' for service [%s]\n",s, lp_servicename(snum))); TALLOC_FREE(s); } /* * New code to check if there's a share security descripter * added from NT server manager. This is done after the * smb.conf checks are done as we need a uid and token. JRA. * */ { bool can_write = False; NT_USER_TOKEN *token = conn->nt_user_token ? conn->nt_user_token : (vuser ? vuser->nt_user_token : NULL); /* * 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); if (!can_write) { if (!share_access_check(token, lp_servicename(snum), FILE_READ_DATA)) { /* No access, read or write. */ DEBUG(0,("make_connection: connection to %s " "denied due to security " "descriptor.\n", lp_servicename(snum))); conn_free(conn); *status = NT_STATUS_ACCESS_DENIED; return NULL; } else { conn->read_only = True; } } } /* Initialise VFS function pointers */ if (!smbd_vfs_init(conn)) { DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(snum))); conn_free(conn); *status = NT_STATUS_BAD_NETWORK_NAME; return NULL; } /* * If widelinks are disallowed we need to canonicalise the connect * path here to ensure we don't have any symlinks in the * connectpath. We will be checking all paths on this connection are * below this directory. We must do this after the VFS init as we * depend on the realpath() pointer in the vfs table. JRA. */ if (!lp_widelinks(snum)) { 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); *status = NT_STATUS_BAD_NETWORK_NAME; return NULL; } } if ((!conn->printer) && (!conn->ipc)) { conn->notify_ctx = notify_init(conn->mem_ctx, server_id_self(), smbd_messaging_context(), smbd_event_context(), conn); } /* ROOT Activities: */ /* * 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; 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); *status = NT_STATUS_INTERNAL_DB_ERROR; return NULL; } /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "root preexec = " line */ if (*lp_rootpreexec(snum)) { char *cmd = talloc_sub_advanced(talloc_tos(), lp_servicename(SNUM(conn)), conn->user, conn->connectpath, conn->gid, get_current_username(), current_user_info.domain, 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; return NULL; } } /* USER Activites: */ if (!change_to_user(conn, conn->vuid)) { /* No point continuing if they fail the basic checks */ DEBUG(0,("Can't become connected user!\n")); yield_connection(conn, lp_servicename(snum)); conn_free(conn); *status = NT_STATUS_LOGON_FAILURE; return NULL; } /* Remember that a different vuid can connect later without these * checks... */ /* Preexecs are done here as they might make the dir we are to ChDir * to below */ /* execute any "preexec = " line */ if (*lp_preexec(snum)) { char *cmd = talloc_sub_advanced(talloc_tos(), lp_servicename(SNUM(conn)), conn->user, conn->connectpath, conn->gid, get_current_username(), current_user_info.domain, 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)); *status = NT_STATUS_ACCESS_DENIED; goto err_root_exit; } } #ifdef WITH_FAKE_KASERVER if (lp_afs_share(snum)) { afs_login(conn); } #endif /* Add veto/hide lists */ if (!IS_IPC(conn) && !IS_PRINT(conn)) { 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) { DEBUG(0,("make_connection: VFS make connection failed!\n")); *status = NT_STATUS_UNSUCCESSFUL; goto err_root_exit; } /* Any error exit after here needs to call the disconnect hook. */ on_err_call_dis_hook = true; /* win2000 does not check the permissions on the directory during the tree connect, instead relying on permission 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)) { DEBUG(0,("'%s' is not a directory, when connecting to " "[%s]\n", conn->connectpath, lp_servicename(snum))); } else { DEBUG(0,("'%s' does not exist or permission denied " "when connecting to [%s] Error was %s\n", conn->connectpath, lp_servicename(snum), strerror(errno) )); } *status = 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) { 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 /* 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); /* * Print out the 'connected as' stuff here as we need * to know the effective uid and gid we will be using * (at least initially). */ 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( "connect to service %s ", lp_servicename(snum) ); dbgtext( "initially as user %s ", user ); 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); err_root_exit: change_to_root_user(); if (on_err_call_dis_hook) { /* Call VFS disconnect hook */ SMB_VFS_DISCONNECT(conn); } yield_connection(conn, lp_servicename(snum)); conn_free(conn); return NULL; }
static NTSTATUS smbd_smb2_auth_generic_return(struct smbXsrv_session *session, struct smbd_smb2_request *smb2req, uint8_t in_security_mode, struct auth_session_info *session_info, uint16_t *out_session_flags, uint64_t *out_session_id) { NTSTATUS status; bool guest = false; uint8_t session_key[16]; struct smbXsrv_session *x = session; struct smbXsrv_connection *xconn = smb2req->xconn; if ((in_security_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) || lp_server_signing() == SMB_SIGNING_REQUIRED) { x->global->signing_required = true; } if (lp_smb_encrypt(-1) == SMB_SIGNING_REQUIRED) { x->global->encryption_required = true; } if (security_session_user_level(session_info, NULL) < SECURITY_USER) { /* we map anonymous to guest internally */ *out_session_flags |= SMB2_SESSION_FLAG_IS_GUEST; *out_session_flags |= SMB2_SESSION_FLAG_IS_NULL; /* force no signing */ x->global->signing_required = false; guest = true; } if (guest && x->global->encryption_required) { DEBUG(1,("reject guest session as encryption is required\n")); return NT_STATUS_ACCESS_DENIED; } if (xconn->smb2.server.cipher == 0) { if (x->global->encryption_required) { DEBUG(1,("reject session with dialect[0x%04X] " "as encryption is required\n", xconn->smb2.server.dialect)); return NT_STATUS_ACCESS_DENIED; } } if (x->global->encryption_required) { *out_session_flags |= SMB2_SESSION_FLAG_ENCRYPT_DATA; } ZERO_STRUCT(session_key); memcpy(session_key, session_info->session_key.data, MIN(session_info->session_key.length, sizeof(session_key))); x->global->signing_key = data_blob_talloc(x->global, session_key, sizeof(session_key)); if (x->global->signing_key.data == NULL) { ZERO_STRUCT(session_key); return NT_STATUS_NO_MEMORY; } if (xconn->protocol >= PROTOCOL_SMB2_24) { const DATA_BLOB label = data_blob_string_const_null("SMB2AESCMAC"); const DATA_BLOB context = data_blob_string_const_null("SmbSign"); smb2_key_derivation(session_key, sizeof(session_key), label.data, label.length, context.data, context.length, x->global->signing_key.data); } if (xconn->protocol >= PROTOCOL_SMB2_24) { const DATA_BLOB label = data_blob_string_const_null("SMB2AESCCM"); const DATA_BLOB context = data_blob_string_const_null("ServerIn "); x->global->decryption_key = data_blob_talloc(x->global, session_key, sizeof(session_key)); if (x->global->decryption_key.data == NULL) { ZERO_STRUCT(session_key); return NT_STATUS_NO_MEMORY; } smb2_key_derivation(session_key, sizeof(session_key), label.data, label.length, context.data, context.length, x->global->decryption_key.data); } if (xconn->protocol >= PROTOCOL_SMB2_24) { const DATA_BLOB label = data_blob_string_const_null("SMB2AESCCM"); const DATA_BLOB context = data_blob_string_const_null("ServerOut"); x->global->encryption_key = data_blob_talloc(x->global, session_key, sizeof(session_key)); if (x->global->encryption_key.data == NULL) { ZERO_STRUCT(session_key); return NT_STATUS_NO_MEMORY; } smb2_key_derivation(session_key, sizeof(session_key), label.data, label.length, context.data, context.length, x->global->encryption_key.data); generate_random_buffer((uint8_t *)&x->nonce_high, sizeof(x->nonce_high)); x->nonce_low = 1; } x->global->application_key = data_blob_dup_talloc(x->global, x->global->signing_key); if (x->global->application_key.data == NULL) { ZERO_STRUCT(session_key); return NT_STATUS_NO_MEMORY; } if (xconn->protocol >= PROTOCOL_SMB2_24) { const DATA_BLOB label = data_blob_string_const_null("SMB2APP"); const DATA_BLOB context = data_blob_string_const_null("SmbRpc"); smb2_key_derivation(session_key, sizeof(session_key), label.data, label.length, context.data, context.length, x->global->application_key.data); } ZERO_STRUCT(session_key); x->global->channels[0].signing_key = data_blob_dup_talloc(x->global->channels, x->global->signing_key); if (x->global->channels[0].signing_key.data == NULL) { return NT_STATUS_NO_MEMORY; } data_blob_clear_free(&session_info->session_key); session_info->session_key = data_blob_dup_talloc(session_info, x->global->application_key); if (session_info->session_key.data == NULL) { return NT_STATUS_NO_MEMORY; } session->compat = talloc_zero(session, struct user_struct); if (session->compat == NULL) { return NT_STATUS_NO_MEMORY; } session->compat->session = session; session->compat->homes_snum = -1; session->compat->session_info = session_info; session->compat->session_keystr = NULL; session->compat->vuid = session->global->session_wire_id; DLIST_ADD(smb2req->sconn->users, session->compat); smb2req->sconn->num_users++; if (security_session_user_level(session_info, NULL) >= SECURITY_USER) { session->compat->homes_snum = register_homes_share(session_info->unix_info->unix_name); } set_current_user_info(session_info->unix_info->sanitized_username, session_info->unix_info->unix_name, session_info->info->domain_name); reload_services(smb2req->sconn, conn_snum_used, true); session->status = NT_STATUS_OK; session->global->auth_session_info = session_info; session->global->auth_session_info_seqnum += 1; session->global->channels[0].auth_session_info_seqnum = session->global->auth_session_info_seqnum; session->global->auth_time = timeval_to_nttime(&smb2req->request_time); session->global->expiration_time = gensec_expire_time(session->gensec); if (!session_claim(session)) { DEBUG(1, ("smb2: Failed to claim session " "for vuid=%llu\n", (unsigned long long)session->compat->vuid)); return NT_STATUS_LOGON_FAILURE; } status = smbXsrv_session_update(session); if (!NT_STATUS_IS_OK(status)) { DEBUG(0, ("smb2: Failed to update session for vuid=%llu - %s\n", (unsigned long long)session->compat->vuid, nt_errstr(status))); return NT_STATUS_LOGON_FAILURE; } /* * we attach the session to the request * so that the response can be signed */ smb2req->session = session; if (!guest) { smb2req->do_signing = true; } global_client_caps |= (CAP_LEVEL_II_OPLOCKS|CAP_STATUS32); *out_session_id = session->global->session_wire_id; return NT_STATUS_OK; }