From e72ecdc862804339912325fe848401e8ec57cde7 Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Mon, 24 Feb 2003 02:35:54 +0000 Subject: Merge of server-side authentication changes to 3.0: - user_ok() and user_in_group() now take a list of groups, instead of looking for the user in the members of all groups. - The 'server_info' returned from the authentication is now kept around - in future we won't copy the sesion key, username etc, we will just referece them directly. - rhosts upgraded to use the SAM if possible, otherwise fake up based on getpwnam(). - auth_util code to deal with groups upgraded to deal with non-winbind domain members again. Andrew Bartlett (This used to be commit 74b5436c75114170ce7c780c19226103d0df9060) --- source3/smbd/password.c | 59 +++++++++++++++++++++++++++++++--------------- source3/smbd/posix_acls.c | 2 +- source3/smbd/service.c | 22 ++++++++--------- source3/smbd/sesssetup.c | 60 ++++++++++++++++++++--------------------------- source3/smbd/uid.c | 4 ++-- 5 files changed, 79 insertions(+), 68 deletions(-) (limited to 'source3/smbd') diff --git a/source3/smbd/password.c b/source3/smbd/password.c index 4ce99e96bb..784c1525c8 100644 --- a/source3/smbd/password.c +++ b/source3/smbd/password.c @@ -62,11 +62,15 @@ void invalidate_vuid(uint16 vuid) if (vuser == NULL) return; - + SAFE_FREE(vuser->homedir); - + SAFE_FREE(vuser->unix_homedir); + SAFE_FREE(vuser->logon_script); + session_yield(vuser); + free_server_info(&vuser->server_info); + DLIST_REMOVE(validated_users, vuser); /* clear the vuid from the 'cache' on each connection, and @@ -93,11 +97,15 @@ void invalidate_all_vuids(void) } } -/**************************************************************************** -register a uid/name pair as being valid and that a valid password -has been given. vuid is biased by an offset. This allows us to -tell random client vuid's (normally zero) from valid vuids. -****************************************************************************/ +/** + * register that a valid login has been performed, establish 'session'. + * @param server_info The token returned from the authentication process. + * (now 'owned' by register_vuid) + * + * @return Newly allocated vuid, biased by an offset. (This allows us to + * tell random client vuid's (normally zero) from valid vuids.) + * + */ int register_vuid(auth_serversupplied_info *server_info, const char *smb_name) { @@ -136,6 +144,7 @@ int register_vuid(auth_serversupplied_info *server_info, const char *smb_name) if (!IS_SAM_UNIX_USER(server_info->sam_account)) { DEBUG(0,("Attempted session setup with invalid user. No uid/gid in SAM_ACCOUNT\n")); free(vuser); + free_server_info(&server_info); return UID_FIELD_INVALID; } @@ -147,20 +156,24 @@ int register_vuid(auth_serversupplied_info *server_info, const char *smb_name) if (!(vuser->groups = memdup(server_info->groups, sizeof(gid_t) * vuser->n_groups))) { DEBUG(0,("register_vuid: failed to memdup vuser->groups\n")); free(vuser); + free_server_info(&server_info); return UID_FIELD_INVALID; } } vuser->guest = server_info->guest; fstrcpy(vuser->user.unix_name, pdb_get_username(server_info->sam_account)); - fstrcpy(vuser->user.smb_name, smb_name); + + /* This is a potentially untrusted username */ + alpha_strcpy(vuser->user.smb_name, smb_name, ". _-$", sizeof(vuser->user.smb_name)); + fstrcpy(vuser->user.domain, pdb_get_domain(server_info->sam_account)); fstrcpy(vuser->user.full_name, pdb_get_fullname(server_info->sam_account)); { /* Keep the homedir handy */ const char *homedir = pdb_get_homedir(server_info->sam_account); - const char *unix_homedir = pdb_get_unix_homedir(server_info->sam_account); /* should be optained by SMS */ + const char *unix_homedir = pdb_get_unix_homedir(server_info->sam_account); const char *logon_script = pdb_get_logon_script(server_info->sam_account); if (homedir) { vuser->homedir = smb_xstrdup(homedir); @@ -188,10 +201,18 @@ int register_vuid(auth_serversupplied_info *server_info, const char *smb_name) vuser->nt_user_token = dup_nt_token(server_info->ptok); } else { DEBUG(1, ("server_info does not contain a user_token - cannot continue\n")); - free(vuser); + free_server_info(&server_info); + SAFE_FREE(vuser->homedir); + SAFE_FREE(vuser->unix_homedir); + SAFE_FREE(vuser->logon_script); + + SAFE_FREE(vuser); return UID_FIELD_INVALID; } + /* use this to keep tabs on all our info from the authentication */ + vuser->server_info = server_info; + DEBUG(3,("UNIX uid %d is UNIX user %s, and will be vuid %u\n",(int)vuser->uid,vuser->user.unix_name, vuser->vuid)); next_vuid++; @@ -246,7 +267,7 @@ void add_session_user(const char *user) /**************************************************************************** check if a username is valid ****************************************************************************/ -BOOL user_ok(const char *user,int snum) +BOOL user_ok(const char *user,int snum, gid_t *groups, size_t n_groups) { char **valid, **invalid; BOOL ret; @@ -257,7 +278,7 @@ BOOL user_ok(const char *user,int snum) if (lp_invalid_users(snum)) { str_list_copy(&invalid, lp_invalid_users(snum)); if (invalid && str_list_substitute(invalid, "%S", lp_servicename(snum))) { - ret = !user_in_list(user, (const char **)invalid); + ret = !user_in_list(user, (const char **)invalid, groups, n_groups); } } if (invalid) @@ -266,7 +287,7 @@ BOOL user_ok(const char *user,int snum) if (ret && lp_valid_users(snum)) { str_list_copy(&valid, lp_valid_users(snum)); if (valid && str_list_substitute(valid, "%S", lp_servicename(snum))) { - ret = user_in_list(user, (const char **)valid); + ret = user_in_list(user, (const char **)valid, groups, n_groups); } } if (valid) @@ -275,7 +296,7 @@ BOOL user_ok(const char *user,int snum) if (ret && lp_onlyuser(snum)) { char **user_list = str_list_make (lp_username(snum), NULL); if (user_list && str_list_substitute(user_list, "%S", lp_servicename(snum))) { - ret = user_in_list(user, (const char **)user_list); + ret = user_in_list(user, (const char **)user_list, groups, n_groups); } if (user_list) str_list_free (&user_list); } @@ -294,7 +315,7 @@ static char *validate_group(char *group, DATA_BLOB password,int snum) setnetgrent(group); while (getnetgrent(&host, &user, &domain)) { if (user) { - if (user_ok(user, snum) && + if (user_ok(user, snum, NULL, 0) && password_ok(user,password)) { endnetgrent(); return(user); @@ -349,7 +370,7 @@ static char *validate_group(char *group, DATA_BLOB password,int snum) while (*member) { static fstring name; fstrcpy(name,member); - if (user_ok(name,snum) && + if (user_ok(name,snum, NULL, 0) && password_ok(name,password)) { endgrent(); return(&name[0]); @@ -408,7 +429,7 @@ BOOL authorise_login(int snum, fstring user, DATA_BLOB password, auser = strtok(NULL,LIST_SEP)) { fstring user2; fstrcpy(user2,auser); - if (!user_ok(user2,snum)) + if (!user_ok(user2,snum, NULL, 0)) continue; if (password_ok(user2,password)) { @@ -443,7 +464,7 @@ and given password ok (%s)\n", user)); } else { fstring user2; fstrcpy(user2,auser); - if (user_ok(user2,snum) && password_ok(user2,password)) { + if (user_ok(user2,snum, NULL, 0) && password_ok(user2,password)) { ok = True; fstrcpy(user,user2); DEBUG(3,("authorise_login: ACCEPTED: user list username \ @@ -468,7 +489,7 @@ and given password ok (%s)\n", user)); *guest = True; } - if (ok && !user_ok(user,snum)) { + if (ok && !user_ok(user, snum, NULL, 0)) { DEBUG(0,("authorise_login: rejected invalid user %s\n",user)); ok = False; } diff --git a/source3/smbd/posix_acls.c b/source3/smbd/posix_acls.c index ee8a4430e1..93a57925f1 100644 --- a/source3/smbd/posix_acls.c +++ b/source3/smbd/posix_acls.c @@ -573,7 +573,7 @@ static BOOL uid_entry_in_group( canon_ace *uid_ace, canon_ace *group_ace ) * not uids/gids. */ - return user_in_group_list(u_name, g_name ); + return user_in_group_list(u_name, g_name, NULL, 0); } /**************************************************************************** diff --git a/source3/smbd/service.c b/source3/smbd/service.c index 2a41a6db1c..1c7e4017b0 100644 --- a/source3/smbd/service.c +++ b/source3/smbd/service.c @@ -258,7 +258,7 @@ static NTSTATUS share_sanity_checks(int snum, pstring dev) /**************************************************************************** readonly share? ****************************************************************************/ -static void set_read_only(connection_struct *conn) +static void set_read_only(connection_struct *conn, gid_t *groups, size_t n_groups) { char **list; char *service = lp_servicename(conn->service); @@ -271,7 +271,7 @@ static void set_read_only(connection_struct *conn) if (!str_list_substitute(list, "%S", service)) { DEBUG(0, ("ERROR: read list substitution failed\n")); } - if (user_in_list(conn->user, (const char **)list)) + if (user_in_list(conn->user, (const char **)list, groups, n_groups)) conn->read_only = True; str_list_free(&list); } @@ -281,7 +281,7 @@ static void set_read_only(connection_struct *conn) if (!str_list_substitute(list, "%S", service)) { DEBUG(0, ("ERROR: write list substitution failed\n")); } - if (user_in_list(conn->user, (const char **)list)) + if (user_in_list(conn->user, (const char **)list, groups, n_groups)) conn->read_only = False; str_list_free(&list); } @@ -291,7 +291,7 @@ static void set_read_only(connection_struct *conn) /**************************************************************************** admin user check ****************************************************************************/ -static void set_admin_user(connection_struct *conn) +static void set_admin_user(connection_struct *conn, gid_t *groups, size_t n_groups) { /* admin user check */ @@ -299,7 +299,7 @@ static void set_admin_user(connection_struct *conn) marked read_only. Changed as I don't think this is needed, but old code left in case there is a problem here. */ - if (user_in_list(conn->user,lp_admin_users(conn->service)) + if (user_in_list(conn->user,lp_admin_users(conn->service), groups, n_groups) #if 0 && !conn->read_only #endif @@ -364,14 +364,14 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, } 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)", lp_servicename(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(vuser->user.unix_name, snum)) { - DEBUG(2, ("user '%s' (from session setup) not permitted to access this share (%s)", vuser->user.unix_name, lp_servicename(snum))); + if (!user_ok(vuser->user.unix_name, snum, vuser->groups, vuser->n_groups)) { + 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; @@ -427,9 +427,9 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, string_set(&conn->user,user); conn->nt_user_token = NULL; - set_read_only(conn); + set_read_only(conn, vuser ? vuser->groups : NULL, vuser ? vuser->n_groups : 0); - set_admin_user(conn); + set_admin_user(conn, vuser ? vuser->groups : NULL, vuser ? vuser->n_groups : 0); /* * If force user is true, then store the @@ -499,7 +499,7 @@ static connection_struct *make_connection_snum(int snum, user_struct *vuser, * Otherwise, the meaning of the '+' would be ignored. */ if (conn->force_user && user_must_be_member) { - if (user_in_group_list( user, gname )) { + if (user_in_group_list( user, gname, NULL, 0)) { conn->gid = gid; DEBUG(3,("Forced group %s for member %s\n",gname,user)); } diff --git a/source3/smbd/sesssetup.c b/source3/smbd/sesssetup.c index bb7d17be56..eafe805aba 100644 --- a/source3/smbd/sesssetup.c +++ b/source3/smbd/sesssetup.c @@ -148,6 +148,7 @@ static int reply_spnego_kerberos(connection_struct *conn, DATA_BLOB auth_data; auth_serversupplied_info *server_info = NULL; ADS_STRUCT *ads; + BOOL foreign = False; if (!spnego_parse_krb5_wrap(*secblob, &ticket)) { return ERROR_NT(NT_STATUS_LOGON_FAILURE); @@ -168,6 +169,8 @@ static int reply_spnego_kerberos(connection_struct *conn, return ERROR_NT(NT_STATUS_LOGON_FAILURE); } + data_blob_free(&auth_data); + DEBUG(3,("Ticket name is [%s]\n", client)); p = strchr_m(client, '@'); @@ -183,12 +186,20 @@ static int reply_spnego_kerberos(connection_struct *conn, if (!lp_allow_trusted_domains()) { return ERROR_NT(NT_STATUS_LOGON_FAILURE); } - /* this gives a fully qualified user name (ie. with full realm). - that leads to very long usernames, but what else can we do? */ - asprintf(&user, "%s%s%s", p+1, lp_winbind_separator(), client); - } else { - user = strdup(client); + foreign = True; + } + + /* this gives a fully qualified user name (ie. with full realm). + that leads to very long usernames, but what else can we do? */ + asprintf(&user, "%s%s%s", p+1, lp_winbind_separator(), client); + + pw = Get_Pwnam(user); + if (!pw && !foreign) { + pw = Get_Pwnam(client); + SAFE_FREE(user); + user = smb_xstrdup(client); } + ads_destroy(&ads); /* setup the string used by %U */ @@ -196,19 +207,6 @@ static int reply_spnego_kerberos(connection_struct *conn, reload_services(True); - /* the password is good - let them in */ - pw = Get_Pwnam(user); - if (!pw && !strstr(user, lp_winbind_separator())) { - char *user2; - /* try it with a winbind domain prefix */ - asprintf(&user2, "%s%s%s", lp_workgroup(), lp_winbind_separator(), user); - pw = Get_Pwnam(user2); - if (pw) { - free(user); - user = user2; - } - } - if (!pw) { DEBUG(1,("Username %s is invalid on this system\n",user)); return ERROR_NT(NT_STATUS_NO_SUCH_USER); @@ -219,10 +217,10 @@ static int reply_spnego_kerberos(connection_struct *conn, return ERROR_NT(ret); } + /* register_vuid keeps the server info */ sess_vuid = register_vuid(server_info, user); free(user); - free_server_info(&server_info); if (sess_vuid == -1) { return ERROR_NT(NT_STATUS_LOGON_FAILURE); @@ -263,8 +261,10 @@ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *outbuf, if (NT_STATUS_IS_OK(nt_status)) { int sess_vuid; - sess_vuid = register_vuid(server_info, (*auth_ntlmssp_state)->ntlmssp_state->user /* check this for weird */); - + /* register_vuid keeps the server info */ + sess_vuid = register_vuid(server_info, (*auth_ntlmssp_state)->ntlmssp_state->user); + (*auth_ntlmssp_state)->server_info = NULL; + if (sess_vuid == -1) { nt_status = NT_STATUS_LOGON_FAILURE; } else { @@ -272,7 +272,7 @@ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *outbuf, set_message(outbuf,4,0,True); SSVAL(outbuf, smb_vwv3, 0); - if ((*auth_ntlmssp_state)->server_info && (*auth_ntlmssp_state)->server_info->guest) { + if (server_info->guest) { SSVAL(outbuf,smb_vwv2,1); } @@ -285,7 +285,7 @@ static BOOL reply_spnego_ntlmssp(connection_struct *conn, char *outbuf, data_blob_free(&response); if (!ret || !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { - auth_ntlmssp_end(&global_ntlmssp_state); + auth_ntlmssp_end(auth_ntlmssp_state); } return ret; @@ -463,7 +463,6 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, extern BOOL global_encrypted_passwords_negotiated; extern BOOL global_spnego_negotiated; extern int Protocol; - extern userdom_struct current_user_info; extern int max_send; auth_usersupplied_info *user_info = NULL; @@ -584,13 +583,6 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, domain,native_os,native_lanman)); } - /* don't allow for weird usernames or domains */ - alpha_strcpy(user, user, ". _-$", sizeof(user)); - alpha_strcpy(domain, domain, ". _-@", sizeof(domain)); - if (strstr(user, "..") || strstr(domain,"..")) { - return ERROR_NT(NT_STATUS_LOGON_FAILURE); - } - DEBUG(3,("sesssetupX:name=[%s]\\[%s]@[%s]\n", domain, user, get_remote_machine_name())); if (*user) { @@ -609,7 +601,7 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, fstrcpy(sub_user, lp_guestaccount()); } - fstrcpy(current_user_info.smb_name,sub_user); + sub_set_smb_name(sub_user); reload_services(True); @@ -692,15 +684,13 @@ int reply_sesssetup_and_X(connection_struct *conn, char *inbuf,char *outbuf, /* register the name and uid as being validated, so further connections to a uid can get through without a password, on the same VC */ + /* register_vuid keeps the server info */ sess_vuid = register_vuid(server_info, sub_user); - - free_server_info(&server_info); if (sess_vuid == -1) { return ERROR_NT(NT_STATUS_LOGON_FAILURE); } - SSVAL(outbuf,smb_uid,sess_vuid); SSVAL(inbuf,smb_uid,sess_vuid); diff --git a/source3/smbd/uid.c b/source3/smbd/uid.c index 7c0d3805e7..6ac3528b1a 100644 --- a/source3/smbd/uid.c +++ b/source3/smbd/uid.c @@ -60,7 +60,7 @@ BOOL change_to_guest(void) static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum) { - int i; + unsigned i; for (i=0;ivuid_cache.entries && i< VUID_CACHE_SIZE;i++) if (conn->vuid_cache.list[i] == vuser->vuid) return(True); @@ -70,7 +70,7 @@ static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum) return False; } - if (!user_ok(vuser->user.unix_name,snum)) + if (!user_ok(vuser->user.unix_name,snum, vuser->groups, vuser->n_groups)) return(False); if (!share_access_check(conn, snum, vuser, conn->read_only ? FILE_READ_DATA : FILE_WRITE_DATA)) { -- cgit