use "winbind separator" in tng as well
[ira/wip.git] / source3 / nsswitch / winbindd_user.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 2.0
4
5    Winbind daemon - user related function
6
7    Copyright (C) Tim Potter 2000
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "winbindd.h"
25
26 /* Fill a pwent structure with information we have obtained */
27
28 static void winbindd_fill_pwent(struct winbindd_pw *pw, char *name,
29                                 uid_t unix_uid, gid_t unix_gid, 
30                                 char *full_name)
31 {
32     pstring homedir;
33     fstring name_domain, name_user;
34
35     if (!pw || !name) {
36         return;
37     }
38
39     /* Fill in uid/gid */
40
41     pw->pw_uid = unix_uid;
42     pw->pw_gid = unix_gid;
43
44     /* Username */
45
46     safe_strcpy(pw->pw_name, name, sizeof(pw->pw_name) - 1);
47
48     /* Full name (gecos) */
49
50     safe_strcpy(pw->pw_gecos, full_name, sizeof(pw->pw_gecos) - 1);
51
52     /* Home directory and shell - use template config parameters.  The
53        defaults are /tmp for the home directory and /bin/false for shell. */
54
55     parse_domain_user(name, name_domain, name_user);
56
57     pstrcpy(homedir, lp_template_homedir());
58
59     pstring_sub(homedir, "%U", name_user);
60     pstring_sub(homedir, "%D", name_domain);
61
62     safe_strcpy(pw->pw_dir, homedir, sizeof(pw->pw_dir) - 1);
63
64     safe_strcpy(pw->pw_shell, lp_template_shell(), sizeof(pw->pw_shell) - 1);
65
66     /* Password - set to "x" as we can't generate anything useful here.
67        Authentication can be done using the pam_ntdom module. */
68
69     safe_strcpy(pw->pw_passwd, "x", sizeof(pw->pw_passwd) - 1);
70 }
71
72 /* Return a password structure from a username.  Specify whether cached data 
73    can be returned. */
74
75 enum winbindd_result winbindd_getpwnam_from_user(struct winbindd_cli_state *state) 
76 {
77     uint32 name_type, user_rid, group_rid;
78     SAM_USERINFO_CTR user_info;
79     DOM_SID user_sid;
80     fstring name_domain, name_user, name, gecos_name;
81     struct winbindd_domain *domain;
82     uid_t uid;
83     gid_t gid;
84
85     /* Parse domain and username */
86     parse_domain_user(state->request.data.username, name_domain, name_user);
87
88     /* Reject names that don't have a domain - i.e name_domain contains the
89        entire name. */
90  
91     if (strequal(name_domain, "")) {
92         return WINBINDD_ERROR;
93     }
94
95     /* Get info for the domain */
96
97     if ((domain = find_domain_from_name(name_domain)) == NULL) {
98         DEBUG(0, ("could not find domain entry for domain %s\n", name_domain));
99         return WINBINDD_ERROR;
100     }
101
102     /* Check for cached user entry */
103
104     if (winbindd_fetch_user_cache_entry(name_domain, name_user,
105                                         &state->response.data.pw)) {
106             return WINBINDD_OK;
107     }
108
109     slprintf(name,sizeof(name),"%s\\%s", name_domain, name_user);
110
111     /* Get rid and name type from name */
112     /* the following costs 1 packet */
113     if (!winbindd_lookup_sid_by_name(domain, name, &user_sid, &name_type)) {
114         DEBUG(1, ("user '%s' does not exist\n", name_user));
115         return WINBINDD_ERROR;
116     }
117
118     if (name_type != SID_NAME_USER) {
119         DEBUG(1, ("name '%s' is not a user name: %d\n", name_user, name_type));
120         return WINBINDD_ERROR;
121     }
122
123     /* Get some user info.  Split the user rid from the sid obtained from
124        the winbind_lookup_by_name() call and use it in a
125        winbind_lookup_userinfo() */
126     
127     sid_split_rid(&user_sid, &user_rid);
128
129     /* the following costs 3 packets */
130     if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
131         DEBUG(1, ("pwnam_from_user(): error getting user info for user '%s'\n",
132                   name_user));
133         return WINBINDD_ERROR;
134     }
135     
136     group_rid = user_info.info.id21->group_rid;
137     unistr2_to_ascii(gecos_name, &user_info.info.id21->uni_full_name,
138                      sizeof(gecos_name) - 1);
139
140     free_samr_userinfo_ctr(&user_info);
141
142     /* Resolve the uid number */
143
144     if (!winbindd_idmap_get_uid_from_rid(domain->name, user_rid, &uid)) {
145         DEBUG(1, ("error getting user id for user %s\n", name_user));
146         return WINBINDD_ERROR;
147     }
148
149     /* Resolve the gid number */   
150
151     if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
152         DEBUG(1, ("error getting group id for user %s\n", name_user));
153         return WINBINDD_ERROR;
154     }
155
156     /* Now take all this information and fill in a passwd structure */
157             
158     winbindd_fill_pwent(&state->response.data.pw, 
159                         state->request.data.username, uid, gid, 
160                         gecos_name);
161             
162     winbindd_fill_user_cache_entry(name_domain, name_user, 
163                                    &state->response.data.pw);
164
165     return WINBINDD_OK;
166 }       
167
168 /* Return a password structure given a uid number */
169
170 enum winbindd_result winbindd_getpwnam_from_uid(struct winbindd_cli_state 
171                                                 *state)
172 {
173     DOM_SID user_sid;
174     struct winbindd_domain *domain;
175     uint32 user_rid, group_rid;
176     fstring user_name, gecos_name;
177     enum SID_NAME_USE name_type;
178     SAM_USERINFO_CTR user_info;
179     gid_t gid;
180
181     /* Get rid from uid */
182     if (!winbindd_idmap_get_rid_from_uid(state->request.data.uid, &user_rid,
183                                          &domain)) {
184         DEBUG(1, ("Could not convert uid %d to rid\n", 
185                   state->request.data.uid));
186         return WINBINDD_ERROR;
187     }
188     
189     /* Check for cached uid entry */
190     if (winbindd_fetch_uid_cache_entry(domain->name, state->request.data.uid,
191                                        &state->response.data.pw)) {
192             return WINBINDD_OK;
193     }
194
195
196     /* Get name and name type from rid */
197
198     sid_copy(&user_sid, &domain->sid);
199     sid_append_rid(&user_sid, user_rid);
200
201     if (!winbindd_lookup_name_by_sid(domain, &user_sid, user_name, 
202                                      &name_type)) {
203         fstring temp;
204
205         sid_to_string(temp, &user_sid);
206         DEBUG(1, ("Could not lookup sid %s\n", temp));
207         return WINBINDD_ERROR;
208     }
209
210     if (strcmp("\\", lp_winbind_separator())) {
211             string_sub(user_name, "\\", lp_winbind_separator(), sizeof(fstring));
212     }
213
214     /* Get some user info */
215     
216     if (!winbindd_lookup_userinfo(domain, user_rid, &user_info)) {
217         DEBUG(1, ("pwnam_from_uid(): error getting user info for user '%s'\n",
218                   user_name));
219         return WINBINDD_ERROR;
220     }
221
222     group_rid = user_info.info.id21->group_rid;
223     unistr2_to_ascii(gecos_name, &user_info.info.id21->uni_full_name,
224                      sizeof(gecos_name) - 1);
225
226     free_samr_userinfo_ctr(&user_info);
227
228     /* Resolve gid number */
229
230     if (!winbindd_idmap_get_gid_from_rid(domain->name, group_rid, &gid)) {
231         DEBUG(1, ("error getting group id for user %s\n", user_name));
232         return WINBINDD_ERROR;
233     }
234
235     /* Fill in password structure */
236
237     winbindd_fill_pwent(&state->response.data.pw, user_name, 
238                         state->request.data.uid, gid, gecos_name);
239
240     winbindd_fill_uid_cache_entry(domain->name, state->request.data.uid,
241                                   &state->response.data.pw);
242
243     return WINBINDD_OK;
244 }
245
246 /*
247  * set/get/endpwent functions
248  */
249
250 /* Rewind file pointer for ntdom passwd database */
251
252 enum winbindd_result winbindd_setpwent(struct winbindd_cli_state *state)
253 {
254     struct winbindd_domain *tmp;
255
256     if (state == NULL) return WINBINDD_ERROR;
257     
258     /* Free old static data if it exists */
259
260     if (state->getpwent_state != NULL) {
261         free_getent_state(state->getpwent_state);
262         state->getpwent_state = NULL;
263     }
264
265     /* Create sam pipes for each domain we know about */
266
267     for(tmp = domain_list; tmp != NULL; tmp = tmp->next) {
268         struct getent_state *domain_state;
269
270         /* Skip domains other than WINBINDD_DOMAIN environment variable */
271
272         if ((strcmp(state->request.domain, "") != 0) &&
273             (strcmp(state->request.domain, tmp->name) != 0)) {
274                 continue;
275         }
276
277         /* Create a state record for this domain */
278
279         if ((domain_state = (struct getent_state *)
280              malloc(sizeof(struct getent_state))) == NULL) {
281
282             return WINBINDD_ERROR;
283         }
284
285         ZERO_STRUCTP(domain_state);
286         domain_state->domain = tmp;
287
288         /* Add to list of open domains */
289
290         DLIST_ADD(state->getpwent_state, domain_state)
291     }
292
293     return WINBINDD_OK;
294 }
295
296 /* Close file pointer to ntdom passwd database */
297
298 enum winbindd_result winbindd_endpwent(struct winbindd_cli_state *state)
299 {
300     if (state == NULL) return WINBINDD_ERROR;
301
302     free_getent_state(state->getpwent_state);    
303     state->getpwent_state = NULL;
304
305     return WINBINDD_OK;
306 }
307
308 /* Fetch next passwd entry from ntdom database */
309
310 enum winbindd_result winbindd_getpwent(struct winbindd_cli_state *state)
311 {
312     if (state == NULL) return WINBINDD_ERROR;
313
314     /* Process the current head of the getent_state list */
315
316     while(state->getpwent_state != NULL) {
317         struct getent_state *ent = state->getpwent_state;
318
319         /* Get list of user entries for this pipe */
320
321         if (!ent->got_sam_entries) {
322             uint32 status, start_ndx = 0;
323
324             /* Look in cache for entries, else get them direct */
325
326             if (!winbindd_fetch_user_cache(ent->domain->name, 
327                                            &ent->sam_entries,
328                                            &ent->num_sam_entries)) {
329
330                 /* Fetch the user entries */
331
332                 if (!domain_handles_open(ent->domain)) goto cleanup;
333
334                 do {
335                     status =
336                         samr_enum_dom_users(
337                             &ent->domain->sam_dom_handle, &start_ndx, 0, 0, 
338                             0x10000, &ent->sam_entries, &ent->num_sam_entries);
339                 } while (status == STATUS_MORE_ENTRIES);
340
341                 /* Fill cache with received entries */
342             
343                 winbindd_fill_user_cache(ent->domain->name, ent->sam_entries, 
344                                          ent->num_sam_entries);
345             }
346             
347             ent->got_sam_entries = True;
348         }
349         
350         /* Send back a user */
351
352         while (ent->sam_entry_index < ent->num_sam_entries) {
353             enum winbindd_result result;
354             fstring domain_user_name;
355             char *user_name = (ent->sam_entries)
356                 [ent->sam_entry_index].acct_name; 
357                 
358             /* Don't bother with machine accounts */
359
360             if (user_name[strlen(user_name) - 1] == '$') {
361                 ent->sam_entry_index++;
362                 continue;
363             }
364
365             /* Prepend domain to name */
366
367             slprintf(domain_user_name, sizeof(domain_user_name),
368                      "%s%s%s", ent->domain->name, lp_winbind_separator(), user_name);
369                 
370             /* Get passwd entry from user name */
371                 
372             fstrcpy(state->request.data.username, domain_user_name);
373             result = winbindd_getpwnam_from_user(state);
374
375             ent->sam_entry_index++;
376                 
377             /* Return if user lookup worked */
378                 
379             if (result == WINBINDD_OK) {
380                 return result;
381             }
382                 
383             /* Try next user */
384             
385             DEBUG(1, ("could not getpwnam_from_user for username %s\n",
386                       domain_user_name));
387         }
388
389         /* We've exhausted all users for this pipe - close it down and
390            start on the next one. */
391
392     cleanup:
393
394         /* Free mallocated memory for sam entries.  The data stored here
395            may have been allocated from the cache. */
396
397         if (ent->sam_entries != NULL) free(ent->sam_entries);
398         ent->sam_entries = NULL;
399
400         /* Free state information for this domain */
401
402         {
403             struct getent_state *old_ent;
404
405             old_ent = state->getpwent_state;
406             DLIST_REMOVE(state->getpwent_state, state->getpwent_state);
407             free(old_ent);
408         }
409     }
410
411     /* Out of pipes so we're done */
412
413     return WINBINDD_ERROR;
414 }