mirror of
https://github.com/samba-team/samba.git
synced 2024-12-27 03:21:53 +03:00
f9a15ce1a6
Jeremy.
(This used to be commit 94747b4639
)
415 lines
12 KiB
C
415 lines
12 KiB
C
/*
|
|
Unix SMB/Netbios implementation.
|
|
Version 2.0
|
|
|
|
Winbind daemon - user related function
|
|
|
|
Copyright (C) Tim Potter 2000
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "winbindd.h"
|
|
|
|
/* Fill a pwent structure with information we have obtained */
|
|
|
|
static void winbindd_fill_pwent(struct winbindd_pw *pw, char *name,
|
|
uid_t unix_uid, gid_t unix_gid,
|
|
char *full_name)
|
|
{
|
|
pstring homedir;
|
|
fstring name_domain, name_user;
|
|
|
|
if (!pw || !name) {
|
|
return;
|
|
}
|
|
|
|
/* Fill in uid/gid */
|
|
|
|
pw->pw_uid = unix_uid;
|
|
pw->pw_gid = unix_gid;
|
|
|
|
/* Username */
|
|
|
|
safe_strcpy(pw->pw_name, name, sizeof(pw->pw_name) - 1);
|
|
|
|
/* Full name (gecos) */
|
|
|
|
safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
|
|
|
|
/* Home directory and shell - use template config parameters. The
|
|
defaults are /tmp for the home directory and /bin/false for shell. */
|
|
|
|
parse_domain_user(name, name_domain, name_user);
|
|
|
|
pstrcpy(homedir, lp_template_homedir());
|
|
|
|
pstring_sub(homedir, "%U", name_user);
|
|
pstring_sub(homedir, "%D", name_domain);
|
|
|
|
safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
|
|
|
|
safe_strcpy(pw->pw_shell, lp_template_shell(), sizeof(pw->pw_shell) - 1);
|
|
|
|
/* Password - set to "x" as we can't generate anything useful here.
|
|
Authentication can be done using the pam_ntdom module. */
|
|
|
|
safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
|
|
}
|
|
|
|
/* Return a password structure from a username. Specify whether cached data
|
|
can be returned. */
|
|
|
|
enum winbindd_result winbindd_getpwnam_from_user(struct winbindd_cli_state *state)
|
|
{
|
|
uint32 name_type, user_rid, group_rid;
|
|
SAM_USERINFO_CTR user_info;
|
|
DOM_SID user_sid;
|
|
fstring name_domain, name_user, name, gecos_name;
|
|
struct winbindd_domain *domain;
|
|
uid_t uid;
|
|
gid_t gid;
|
|
|
|
/* Parse domain and username */
|
|
parse_domain_user(state->request.data.username, name_domain, name_user);
|
|
|
|
/* Reject names that don't have a domain - i.e name_domain contains the
|
|
entire name. */
|
|
|
|
if (strequal(name_domain, "")) {
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
/* Get info for the domain */
|
|
|
|
if ((domain = find_domain_from_name(name_domain)) == NULL) {
|
|
DEBUG(0, ("could not find domain entry for domain %s\n", name_domain));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
/* Check for cached user entry */
|
|
|
|
if (winbindd_fetch_user_cache_entry(name_domain, name_user,
|
|
&state->response.data.pw)) {
|
|
return WINBINDD_OK;
|
|
}
|
|
|
|
slprintf(name,sizeof(name)-1,"%s\\%s", name_domain, name_user);
|
|
|
|
/* Get rid and name type from name */
|
|
/* the following costs 1 packet */
|
|
if (!winbindd_lookup_sid_by_name(domain, name, &user_sid, &name_type)) {
|
|
DEBUG(1, ("user '%s' does not exist\n", name_user));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
if (name_type != SID_NAME_USER) {
|
|
DEBUG(1, ("name '%s' is not a user name: %d\n", name_user, name_type));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
/* Get some user info. Split the user rid from the sid obtained from
|
|
the winbind_lookup_by_name() call and use it in a
|
|
winbind_lookup_userinfo() */
|
|
|
|
sid_split_rid(&user_sid, &user_rid);
|
|
|
|
/* the following costs 3 packets */
|
|
if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
|
|
DEBUG(1, ("pwnam_from_user(): error getting user info for user '%s'\n",
|
|
name_user));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
group_rid = user_info.info.id21->group_rid;
|
|
unistr2_to_ascii(gecos_name, &user_info.info.id21->uni_full_name,
|
|
sizeof(gecos_name) - 1);
|
|
|
|
free_samr_userinfo_ctr(&user_info);
|
|
|
|
/* Resolve the uid number */
|
|
|
|
if (!winbindd_idmap_get_uid_from_rid(domain->name, user_rid, &uid)) {
|
|
DEBUG(1, ("error getting user id for user %s\n", name_user));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
/* Resolve the gid number */
|
|
|
|
if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
|
|
DEBUG(1, ("error getting group id for user %s\n", name_user));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
/* Now take all this information and fill in a passwd structure */
|
|
|
|
winbindd_fill_pwent(&state->response.data.pw,
|
|
state->request.data.username, uid, gid,
|
|
gecos_name);
|
|
|
|
winbindd_fill_user_cache_entry(name_domain, name_user,
|
|
&state->response.data.pw);
|
|
|
|
return WINBINDD_OK;
|
|
}
|
|
|
|
/* Return a password structure given a uid number */
|
|
|
|
enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state
|
|
*state)
|
|
{
|
|
DOM_SID user_sid;
|
|
struct winbindd_domain *domain;
|
|
uint32 user_rid, group_rid;
|
|
fstring user_name, gecos_name;
|
|
enum SID_NAME_USE name_type;
|
|
SAM_USERINFO_CTR user_info;
|
|
gid_t gid;
|
|
|
|
/* Get rid from uid */
|
|
if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid, &user_rid,
|
|
&domain)) {
|
|
DEBUG(1, ("Could not convert uid %d to rid\n",
|
|
state->request.data.uid));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
/* Check for cached uid entry */
|
|
if (winbindd_fetch_uid_cache_entry(domain->name, state->request.data.uid,
|
|
&state->response.data.pw)) {
|
|
return WINBINDD_OK;
|
|
}
|
|
|
|
|
|
/* Get name and name type from rid */
|
|
|
|
sid_copy(&user_sid, &domain->sid);
|
|
sid_append_rid(&user_sid, user_rid);
|
|
|
|
if (!winbindd_lookup_name_by_sid(domain, &user_sid, user_name,
|
|
&name_type)) {
|
|
fstring temp;
|
|
|
|
sid_to_string(temp, &user_sid);
|
|
DEBUG(1, ("Could not lookup sid %s\n", temp));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
if (strcmp("\\", lp_winbind_separator())) {
|
|
string_sub(user_name, "\\", lp_winbind_separator(), sizeof(fstring));
|
|
}
|
|
|
|
/* Get some user info */
|
|
|
|
if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
|
|
DEBUG(1, ("pwnam_from_uid(): error getting user info for user '%s'\n",
|
|
user_name));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
group_rid = user_info.info.id21->group_rid;
|
|
unistr2_to_ascii(gecos_name, &user_info.info.id21->uni_full_name,
|
|
sizeof(gecos_name) - 1);
|
|
|
|
free_samr_userinfo_ctr(&user_info);
|
|
|
|
/* Resolve gid number */
|
|
|
|
if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
|
|
DEBUG(1, ("error getting group id for user %s\n", user_name));
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
/* Fill in password structure */
|
|
|
|
winbindd_fill_pwent(&state->response.data.pw, user_name,
|
|
state->request.data.uid, gid, gecos_name);
|
|
|
|
winbindd_fill_uid_cache_entry(domain->name, state->request.data.uid,
|
|
&state->response.data.pw);
|
|
|
|
return WINBINDD_OK;
|
|
}
|
|
|
|
/*
|
|
* set/get/endpwent functions
|
|
*/
|
|
|
|
/* Rewind file pointer for ntdom passwd database */
|
|
|
|
enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
|
|
{
|
|
struct winbindd_domain *tmp;
|
|
|
|
if (state == NULL) return WINBINDD_ERROR;
|
|
|
|
/* Free old static data if it exists */
|
|
|
|
if (state->getpwent_state != NULL) {
|
|
free_getent_state(state->getpwent_state);
|
|
state->getpwent_state = NULL;
|
|
}
|
|
|
|
/* Create sam pipes for each domain we know about */
|
|
|
|
for(tmp = domain_list; tmp != NULL; tmp = tmp->next) {
|
|
struct getent_state *domain_state;
|
|
|
|
/* Skip domains other than WINBINDD_DOMAIN environment variable */
|
|
|
|
if ((strcmp(state->request.domain, "") != 0) &&
|
|
(strcmp(state->request.domain, tmp->name) != 0)) {
|
|
continue;
|
|
}
|
|
|
|
/* Create a state record for this domain */
|
|
|
|
if ((domain_state = (struct getent_state *)
|
|
malloc(sizeof(struct getent_state))) == NULL) {
|
|
|
|
return WINBINDD_ERROR;
|
|
}
|
|
|
|
ZERO_STRUCTP(domain_state);
|
|
domain_state->domain = tmp;
|
|
|
|
/* Add to list of open domains */
|
|
|
|
DLIST_ADD(state->getpwent_state, domain_state)
|
|
}
|
|
|
|
return WINBINDD_OK;
|
|
}
|
|
|
|
/* Close file pointer to ntdom passwd database */
|
|
|
|
enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
|
|
{
|
|
if (state == NULL) return WINBINDD_ERROR;
|
|
|
|
free_getent_state(state->getpwent_state);
|
|
state->getpwent_state = NULL;
|
|
|
|
return WINBINDD_OK;
|
|
}
|
|
|
|
/* Fetch next passwd entry from ntdom database */
|
|
|
|
enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
|
|
{
|
|
if (state == NULL) return WINBINDD_ERROR;
|
|
|
|
/* Process the current head of the getent_state list */
|
|
|
|
while(state->getpwent_state != NULL) {
|
|
struct getent_state *ent = state->getpwent_state;
|
|
|
|
/* Get list of user entries for this pipe */
|
|
|
|
if (!ent->got_sam_entries) {
|
|
uint32 status, start_ndx = 0;
|
|
|
|
/* Look in cache for entries, else get them direct */
|
|
|
|
if (!winbindd_fetch_user_cache(ent->domain->name,
|
|
&ent->sam_entries,
|
|
&ent->num_sam_entries)) {
|
|
|
|
/* Fetch the user entries */
|
|
|
|
if (!domain_handles_open(ent->domain)) goto cleanup;
|
|
|
|
do {
|
|
status =
|
|
samr_enum_dom_users(
|
|
&ent->domain->sam_dom_handle, &start_ndx, 0, 0,
|
|
0x10000, &ent->sam_entries, &ent->num_sam_entries);
|
|
} while (status == STATUS_MORE_ENTRIES);
|
|
|
|
/* Fill cache with received entries */
|
|
|
|
winbindd_fill_user_cache(ent->domain->name, ent->sam_entries,
|
|
ent->num_sam_entries);
|
|
}
|
|
|
|
ent->got_sam_entries = True;
|
|
}
|
|
|
|
/* Send back a user */
|
|
|
|
while (ent->sam_entry_index < ent->num_sam_entries) {
|
|
enum winbindd_result result;
|
|
fstring domain_user_name;
|
|
char *user_name = (ent->sam_entries)
|
|
[ent->sam_entry_index].acct_name;
|
|
|
|
/* Don't bother with machine accounts */
|
|
|
|
if (user_name[strlen(user_name) - 1] == '$') {
|
|
ent->sam_entry_index++;
|
|
continue;
|
|
}
|
|
|
|
/* Prepend domain to name */
|
|
|
|
slprintf(domain_user_name, sizeof(domain_user_name)-1,
|
|
"%s%s%s", ent->domain->name, lp_winbind_separator(), user_name);
|
|
|
|
/* Get passwd entry from user name */
|
|
|
|
fstrcpy(state->request.data.username, domain_user_name);
|
|
result = winbindd_getpwnam_from_user(state);
|
|
|
|
ent->sam_entry_index++;
|
|
|
|
/* Return if user lookup worked */
|
|
|
|
if (result == WINBINDD_OK) {
|
|
return result;
|
|
}
|
|
|
|
/* Try next user */
|
|
|
|
DEBUG(1, ("could not getpwnam_from_user for username %s\n",
|
|
domain_user_name));
|
|
}
|
|
|
|
/* We've exhausted all users for this pipe - close it down and
|
|
start on the next one. */
|
|
|
|
cleanup:
|
|
|
|
/* Free mallocated memory for sam entries. The data stored here
|
|
may have been allocated from the cache. */
|
|
|
|
if (ent->sam_entries != NULL) free(ent->sam_entries);
|
|
ent->sam_entries = NULL;
|
|
|
|
/* Free state information for this domain */
|
|
|
|
{
|
|
struct getent_state *old_ent;
|
|
|
|
old_ent = state->getpwent_state;
|
|
DLIST_REMOVE(state->getpwent_state, state->getpwent_state);
|
|
free(old_ent);
|
|
}
|
|
}
|
|
|
|
/* Out of pipes so we're done */
|
|
|
|
return WINBINDD_ERROR;
|
|
}
|