r21070: * Add the new boolean 'winbind normalize names' option as discussed
[Samba.git] / source / nsswitch / winbindd_util.c
blobb466c66f8677367b9246dad7e9682bdfe3ad65d3
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind daemon for ntdom nss module
6 Copyright (C) Tim Potter 2000-2001
7 Copyright (C) 2001 by Martin Pool <mbp@samba.org>
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.
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.
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.
24 #include "includes.h"
25 #include "winbindd.h"
27 #undef DBGC_CLASS
28 #define DBGC_CLASS DBGC_WINBIND
30 extern struct winbindd_methods cache_methods;
31 extern struct winbindd_methods passdb_methods;
33 /**
34 * @file winbindd_util.c
36 * Winbind daemon for NT domain authentication nss module.
37 **/
40 /**
41 * Used to clobber name fields that have an undefined value.
43 * Correct code should never look at a field that has this value.
44 **/
46 static const fstring name_deadbeef = "<deadbeef>";
48 /* The list of trusted domains. Note that the list can be deleted and
49 recreated using the init_domain_list() function so pointers to
50 individual winbindd_domain structures cannot be made. Keep a copy of
51 the domain name instead. */
53 static struct winbindd_domain *_domain_list;
55 /**
56 When was the last scan of trusted domains done?
58 0 == not ever
61 static time_t last_trustdom_scan;
63 struct winbindd_domain *domain_list(void)
65 /* Initialise list */
67 if ((!_domain_list) && (!init_domain_list())) {
68 smb_panic("Init_domain_list failed\n");
71 return _domain_list;
74 /* Free all entries in the trusted domain list */
76 void free_domain_list(void)
78 struct winbindd_domain *domain = _domain_list;
80 while(domain) {
81 struct winbindd_domain *next = domain->next;
83 DLIST_REMOVE(_domain_list, domain);
84 SAFE_FREE(domain);
85 domain = next;
89 static BOOL is_internal_domain(const DOM_SID *sid)
91 if (sid == NULL)
92 return False;
94 return (sid_check_is_domain(sid) || sid_check_is_builtin(sid));
97 static BOOL is_in_internal_domain(const DOM_SID *sid)
99 if (sid == NULL)
100 return False;
102 return (sid_check_is_in_our_domain(sid) || sid_check_is_in_builtin(sid));
106 /* Add a trusted domain to our list of domains */
107 static struct winbindd_domain *add_trusted_domain(const char *domain_name, const char *alt_name,
108 struct winbindd_methods *methods,
109 const DOM_SID *sid)
111 struct winbindd_domain *domain;
112 const char *alternative_name = NULL;
114 /* ignore alt_name if we are not in an AD domain */
116 if ( (lp_security() == SEC_ADS) && alt_name && *alt_name) {
117 alternative_name = alt_name;
120 /* We can't call domain_list() as this function is called from
121 init_domain_list() and we'll get stuck in a loop. */
122 for (domain = _domain_list; domain; domain = domain->next) {
123 if (strequal(domain_name, domain->name) ||
124 strequal(domain_name, domain->alt_name)) {
125 return domain;
127 if (alternative_name && *alternative_name) {
128 if (strequal(alternative_name, domain->name) ||
129 strequal(alternative_name, domain->alt_name)) {
130 return domain;
133 if (sid) {
134 if (is_null_sid(sid)) {
136 } else if (sid_equal(sid, &domain->sid)) {
137 return domain;
142 /* Create new domain entry */
144 if ((domain = SMB_MALLOC_P(struct winbindd_domain)) == NULL)
145 return NULL;
147 /* Fill in fields */
149 ZERO_STRUCTP(domain);
151 /* prioritise the short name */
152 if (strchr_m(domain_name, '.') && alternative_name && *alternative_name) {
153 fstrcpy(domain->name, alternative_name);
154 fstrcpy(domain->alt_name, domain_name);
155 } else {
156 fstrcpy(domain->name, domain_name);
157 if (alternative_name) {
158 fstrcpy(domain->alt_name, alternative_name);
162 domain->methods = methods;
163 domain->backend = NULL;
164 domain->internal = is_internal_domain(sid);
165 domain->sequence_number = DOM_SEQUENCE_NONE;
166 domain->last_seq_check = 0;
167 domain->initialized = False;
168 domain->online = is_internal_domain(sid);
169 domain->check_online_timeout = 0;
170 if (sid) {
171 sid_copy(&domain->sid, sid);
174 /* Link to domain list */
175 DLIST_ADD(_domain_list, domain);
177 DEBUG(2,("Added domain %s %s %s\n",
178 domain->name, domain->alt_name,
179 &domain->sid?sid_string_static(&domain->sid):""));
181 return domain;
184 /********************************************************************
185 rescan our domains looking for new trusted domains
186 ********************************************************************/
188 struct trustdom_state {
189 TALLOC_CTX *mem_ctx;
190 struct winbindd_response *response;
193 static void trustdom_recv(void *private_data, BOOL success);
195 static void add_trusted_domains( struct winbindd_domain *domain )
197 TALLOC_CTX *mem_ctx;
198 struct winbindd_request *request;
199 struct winbindd_response *response;
201 struct trustdom_state *state;
203 mem_ctx = talloc_init("add_trusted_domains");
204 if (mem_ctx == NULL) {
205 DEBUG(0, ("talloc_init failed\n"));
206 return;
209 request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
210 response = TALLOC_P(mem_ctx, struct winbindd_response);
211 state = TALLOC_P(mem_ctx, struct trustdom_state);
213 if ((request == NULL) || (response == NULL) || (state == NULL)) {
214 DEBUG(0, ("talloc failed\n"));
215 talloc_destroy(mem_ctx);
216 return;
219 state->mem_ctx = mem_ctx;
220 state->response = response;
222 request->length = sizeof(*request);
223 request->cmd = WINBINDD_LIST_TRUSTDOM;
225 async_domain_request(mem_ctx, domain, request, response,
226 trustdom_recv, state);
229 static void trustdom_recv(void *private_data, BOOL success)
231 struct trustdom_state *state =
232 talloc_get_type_abort(private_data, struct trustdom_state);
233 struct winbindd_response *response = state->response;
234 char *p;
236 if ((!success) || (response->result != WINBINDD_OK)) {
237 DEBUG(1, ("Could not receive trustdoms\n"));
238 talloc_destroy(state->mem_ctx);
239 return;
242 p = (char *)response->extra_data.data;
244 while ((p != NULL) && (*p != '\0')) {
245 char *q, *sidstr, *alt_name;
246 DOM_SID sid;
248 alt_name = strchr(p, '\\');
249 if (alt_name == NULL) {
250 DEBUG(0, ("Got invalid trustdom response\n"));
251 break;
254 *alt_name = '\0';
255 alt_name += 1;
257 sidstr = strchr(alt_name, '\\');
258 if (sidstr == NULL) {
259 DEBUG(0, ("Got invalid trustdom response\n"));
260 break;
263 *sidstr = '\0';
264 sidstr += 1;
266 q = strchr(sidstr, '\n');
267 if (q != NULL)
268 *q = '\0';
270 if (!string_to_sid(&sid, sidstr)) {
271 /* Allow NULL sid for sibling domains */
272 if ( strcmp(sidstr,"S-0-0") == 0) {
273 sid_copy( &sid, &global_sid_NULL);
274 } else {
275 DEBUG(0, ("Got invalid trustdom response\n"));
276 break;
280 if (find_domain_from_name_noinit(p) == NULL) {
281 struct winbindd_domain *domain;
282 char *alternate_name = NULL;
284 /* use the real alt_name if we have one, else pass in NULL */
286 if ( !strequal( alt_name, "(null)" ) )
287 alternate_name = alt_name;
289 domain = add_trusted_domain(p, alternate_name,
290 &cache_methods,
291 &sid);
292 setup_domain_child(domain, &domain->child, NULL);
294 p=q;
295 if (p != NULL)
296 p += 1;
299 SAFE_FREE(response->extra_data.data);
300 talloc_destroy(state->mem_ctx);
303 /********************************************************************
304 Periodically we need to refresh the trusted domain cache for smbd
305 ********************************************************************/
307 void rescan_trusted_domains( void )
309 time_t now = time(NULL);
311 /* see if the time has come... */
313 if ((now >= last_trustdom_scan) &&
314 ((now-last_trustdom_scan) < WINBINDD_RESCAN_FREQ) )
315 return;
317 /* this will only add new domains we didn't already know about */
319 add_trusted_domains( find_our_domain() );
321 last_trustdom_scan = now;
323 return;
326 struct init_child_state {
327 TALLOC_CTX *mem_ctx;
328 struct winbindd_domain *domain;
329 struct winbindd_request *request;
330 struct winbindd_response *response;
331 void (*continuation)(void *private_data, BOOL success);
332 void *private_data;
335 static void init_child_recv(void *private_data, BOOL success);
336 static void init_child_getdc_recv(void *private_data, BOOL success);
338 enum winbindd_result init_child_connection(struct winbindd_domain *domain,
339 void (*continuation)(void *private_data,
340 BOOL success),
341 void *private_data)
343 TALLOC_CTX *mem_ctx;
344 struct winbindd_request *request;
345 struct winbindd_response *response;
346 struct init_child_state *state;
347 struct winbindd_domain *request_domain;
349 mem_ctx = talloc_init("init_child_connection");
350 if (mem_ctx == NULL) {
351 DEBUG(0, ("talloc_init failed\n"));
352 return WINBINDD_ERROR;
355 request = TALLOC_ZERO_P(mem_ctx, struct winbindd_request);
356 response = TALLOC_P(mem_ctx, struct winbindd_response);
357 state = TALLOC_P(mem_ctx, struct init_child_state);
359 if ((request == NULL) || (response == NULL) || (state == NULL)) {
360 DEBUG(0, ("talloc failed\n"));
361 TALLOC_FREE(mem_ctx);
362 continuation(private_data, False);
363 return WINBINDD_ERROR;
366 request->length = sizeof(*request);
368 state->mem_ctx = mem_ctx;
369 state->domain = domain;
370 state->request = request;
371 state->response = response;
372 state->continuation = continuation;
373 state->private_data = private_data;
375 if (IS_DC || domain->primary) {
376 /* The primary domain has to find the DC name itself */
377 request->cmd = WINBINDD_INIT_CONNECTION;
378 fstrcpy(request->domain_name, domain->name);
379 request->data.init_conn.is_primary = True;
380 fstrcpy(request->data.init_conn.dcname, "");
381 async_request(mem_ctx, &domain->child, request, response,
382 init_child_recv, state);
383 return WINBINDD_PENDING;
386 /* This is *not* the primary domain, let's ask our DC about a DC
387 * name */
389 request->cmd = WINBINDD_GETDCNAME;
390 fstrcpy(request->domain_name, domain->name);
392 /* save online flag */
393 request_domain = find_our_domain();
394 request_domain->online = domain->online;
396 async_domain_request(mem_ctx, request_domain, request, response,
397 init_child_getdc_recv, state);
398 return WINBINDD_PENDING;
401 static void init_child_getdc_recv(void *private_data, BOOL success)
403 struct init_child_state *state =
404 talloc_get_type_abort(private_data, struct init_child_state);
405 const char *dcname = "";
407 DEBUG(10, ("Received getdcname response\n"));
409 if (success && (state->response->result == WINBINDD_OK)) {
410 dcname = state->response->data.dc_name;
413 state->request->cmd = WINBINDD_INIT_CONNECTION;
414 fstrcpy(state->request->domain_name, state->domain->name);
415 state->request->data.init_conn.is_primary = False;
416 fstrcpy(state->request->data.init_conn.dcname, dcname);
418 async_request(state->mem_ctx, &state->domain->child,
419 state->request, state->response,
420 init_child_recv, state);
423 static void init_child_recv(void *private_data, BOOL success)
425 struct init_child_state *state =
426 talloc_get_type_abort(private_data, struct init_child_state);
428 DEBUG(5, ("Received child initialization response for domain %s\n",
429 state->domain->name));
431 if ((!success) || (state->response->result != WINBINDD_OK)) {
432 DEBUG(3, ("Could not init child\n"));
433 state->continuation(state->private_data, False);
434 talloc_destroy(state->mem_ctx);
435 return;
438 fstrcpy(state->domain->name,
439 state->response->data.domain_info.name);
440 fstrcpy(state->domain->alt_name,
441 state->response->data.domain_info.alt_name);
442 string_to_sid(&state->domain->sid,
443 state->response->data.domain_info.sid);
444 state->domain->native_mode =
445 state->response->data.domain_info.native_mode;
446 state->domain->active_directory =
447 state->response->data.domain_info.active_directory;
448 state->domain->sequence_number =
449 state->response->data.domain_info.sequence_number;
451 init_dc_connection(state->domain);
453 if (state->continuation != NULL)
454 state->continuation(state->private_data, True);
455 talloc_destroy(state->mem_ctx);
458 enum winbindd_result winbindd_dual_init_connection(struct winbindd_domain *domain,
459 struct winbindd_cli_state *state)
461 /* Ensure null termination */
462 state->request.domain_name
463 [sizeof(state->request.domain_name)-1]='\0';
464 state->request.data.init_conn.dcname
465 [sizeof(state->request.data.init_conn.dcname)-1]='\0';
467 if (strlen(state->request.data.init_conn.dcname) > 0) {
468 fstrcpy(domain->dcname, state->request.data.init_conn.dcname);
471 init_dc_connection(domain);
473 if (!domain->initialized) {
474 /* If we return error here we can't do any cached authentication,
475 but we may be in disconnected mode and can't initialize correctly.
476 Do what the previous code did and just return without initialization,
477 once we go online we'll re-initialize.
479 DEBUG(5, ("winbindd_dual_init_connection: %s returning without initialization "
480 "online = %d\n", domain->name, (int)domain->online ));
483 fstrcpy(state->response.data.domain_info.name, domain->name);
484 fstrcpy(state->response.data.domain_info.alt_name, domain->alt_name);
485 fstrcpy(state->response.data.domain_info.sid,
486 sid_string_static(&domain->sid));
488 state->response.data.domain_info.native_mode
489 = domain->native_mode;
490 state->response.data.domain_info.active_directory
491 = domain->active_directory;
492 state->response.data.domain_info.primary
493 = domain->primary;
494 state->response.data.domain_info.sequence_number =
495 domain->sequence_number;
497 return WINBINDD_OK;
500 /* Look up global info for the winbind daemon */
501 BOOL init_domain_list(void)
503 struct winbindd_domain *domain;
504 int role = lp_server_role();
506 /* Free existing list */
507 free_domain_list();
509 /* Add ourselves as the first entry. */
511 if ( role == ROLE_DOMAIN_MEMBER ) {
512 DOM_SID our_sid;
514 if (!secrets_fetch_domain_sid(lp_workgroup(), &our_sid)) {
515 DEBUG(0, ("Could not fetch our SID - did we join?\n"));
516 return False;
519 domain = add_trusted_domain( lp_workgroup(), lp_realm(),
520 &cache_methods, &our_sid);
521 domain->primary = True;
522 setup_domain_child(domain, &domain->child, NULL);
524 /* Even in the parent winbindd we'll need to
525 talk to the DC, so try and see if we can
526 contact it. Theoretically this isn't neccessary
527 as the init_dc_connection() in init_child_recv()
528 will do this, but we can start detecting the DC
529 early here. */
530 set_domain_online_request(domain);
533 /* Local SAM */
535 domain = add_trusted_domain(get_global_sam_name(), NULL,
536 &passdb_methods, get_global_sam_sid());
537 if ( role != ROLE_DOMAIN_MEMBER ) {
538 domain->primary = True;
540 setup_domain_child(domain, &domain->child, NULL);
542 /* BUILTIN domain */
544 domain = add_trusted_domain("BUILTIN", NULL, &passdb_methods,
545 &global_sid_Builtin);
546 setup_domain_child(domain, &domain->child, NULL);
548 return True;
551 /**
552 * Given a domain name, return the struct winbindd domain info for it
554 * @note Do *not* pass lp_workgroup() to this function. domain_list
555 * may modify it's value, and free that pointer. Instead, our local
556 * domain may be found by calling find_our_domain().
557 * directly.
560 * @return The domain structure for the named domain, if it is working.
563 struct winbindd_domain *find_domain_from_name_noinit(const char *domain_name)
565 struct winbindd_domain *domain;
567 /* Search through list */
569 for (domain = domain_list(); domain != NULL; domain = domain->next) {
570 if (strequal(domain_name, domain->name) ||
571 (domain->alt_name[0] &&
572 strequal(domain_name, domain->alt_name))) {
573 return domain;
577 /* Not found */
579 return NULL;
582 struct winbindd_domain *find_domain_from_name(const char *domain_name)
584 struct winbindd_domain *domain;
586 domain = find_domain_from_name_noinit(domain_name);
588 if (domain == NULL)
589 return NULL;
591 if (!domain->initialized)
592 init_dc_connection(domain);
594 return domain;
597 /* Given a domain sid, return the struct winbindd domain info for it */
599 struct winbindd_domain *find_domain_from_sid_noinit(const DOM_SID *sid)
601 struct winbindd_domain *domain;
603 /* Search through list */
605 for (domain = domain_list(); domain != NULL; domain = domain->next) {
606 if (sid_compare_domain(sid, &domain->sid) == 0)
607 return domain;
610 /* Not found */
612 return NULL;
615 /* Given a domain sid, return the struct winbindd domain info for it */
617 struct winbindd_domain *find_domain_from_sid(const DOM_SID *sid)
619 struct winbindd_domain *domain;
621 domain = find_domain_from_sid_noinit(sid);
623 if (domain == NULL)
624 return NULL;
626 if (!domain->initialized)
627 init_dc_connection(domain);
629 return domain;
632 struct winbindd_domain *find_our_domain(void)
634 struct winbindd_domain *domain;
636 /* Search through list */
638 for (domain = domain_list(); domain != NULL; domain = domain->next) {
639 if (domain->primary)
640 return domain;
643 smb_panic("Could not find our domain\n");
644 return NULL;
647 struct winbindd_domain *find_root_domain(void)
649 struct winbindd_domain *ours = find_our_domain();
651 if ( !ours )
652 return NULL;
654 if ( strlen(ours->forest_name) == 0 )
655 return NULL;
657 return find_domain_from_name( ours->forest_name );
660 struct winbindd_domain *find_builtin_domain(void)
662 DOM_SID sid;
663 struct winbindd_domain *domain;
665 string_to_sid(&sid, "S-1-5-32");
666 domain = find_domain_from_sid(&sid);
668 if (domain == NULL)
669 smb_panic("Could not find BUILTIN domain\n");
671 return domain;
674 /* Find the appropriate domain to lookup a name or SID */
676 struct winbindd_domain *find_lookup_domain_from_sid(const DOM_SID *sid)
678 /* A DC can't ask the local smbd for remote SIDs, here winbindd is the
679 * one to contact the external DC's. On member servers the internal
680 * domains are different: These are part of the local SAM. */
682 DEBUG(10, ("find_lookup_domain_from_sid(%s)\n",
683 sid_string_static(sid)));
685 if (IS_DC || is_internal_domain(sid) || is_in_internal_domain(sid)) {
686 DEBUG(10, ("calling find_domain_from_sid\n"));
687 return find_domain_from_sid(sid);
690 /* On a member server a query for SID or name can always go to our
691 * primary DC. */
693 DEBUG(10, ("calling find_our_domain\n"));
694 return find_our_domain();
697 struct winbindd_domain *find_lookup_domain_from_name(const char *domain_name)
699 if (IS_DC || strequal(domain_name, "BUILTIN") ||
700 strequal(domain_name, get_global_sam_name()))
701 return find_domain_from_name_noinit(domain_name);
703 return find_our_domain();
706 /* Lookup a sid in a domain from a name */
708 BOOL winbindd_lookup_sid_by_name(TALLOC_CTX *mem_ctx,
709 struct winbindd_domain *domain,
710 const char *domain_name,
711 const char *name, DOM_SID *sid,
712 enum lsa_SidType *type)
714 NTSTATUS result;
716 /* Lookup name */
717 result = domain->methods->name_to_sid(domain, mem_ctx, domain_name, name, sid, type);
719 /* Return rid and type if lookup successful */
720 if (!NT_STATUS_IS_OK(result)) {
721 *type = SID_NAME_UNKNOWN;
724 return NT_STATUS_IS_OK(result);
728 * @brief Lookup a name in a domain from a sid.
730 * @param sid Security ID you want to look up.
731 * @param name On success, set to the name corresponding to @p sid.
732 * @param dom_name On success, set to the 'domain name' corresponding to @p sid.
733 * @param type On success, contains the type of name: alias, group or
734 * user.
735 * @retval True if the name exists, in which case @p name and @p type
736 * are set, otherwise False.
738 BOOL winbindd_lookup_name_by_sid(TALLOC_CTX *mem_ctx,
739 DOM_SID *sid,
740 fstring dom_name,
741 fstring name,
742 enum lsa_SidType *type)
744 char *names;
745 char *dom_names;
746 NTSTATUS result;
747 BOOL rv = False;
748 struct winbindd_domain *domain;
750 domain = find_lookup_domain_from_sid(sid);
752 if (!domain) {
753 DEBUG(1,("Can't find domain from sid\n"));
754 return False;
757 /* Lookup name */
759 result = domain->methods->sid_to_name(domain, mem_ctx, sid, &dom_names, &names, type);
761 /* Return name and type if successful */
763 if ((rv = NT_STATUS_IS_OK(result))) {
764 fstrcpy(dom_name, dom_names);
765 fstrcpy(name, names);
766 } else {
767 *type = SID_NAME_UNKNOWN;
768 fstrcpy(name, name_deadbeef);
771 return rv;
774 /* Free state information held for {set,get,end}{pw,gr}ent() functions */
776 void free_getent_state(struct getent_state *state)
778 struct getent_state *temp;
780 /* Iterate over state list */
782 temp = state;
784 while(temp != NULL) {
785 struct getent_state *next;
787 /* Free sam entries then list entry */
789 SAFE_FREE(state->sam_entries);
790 DLIST_REMOVE(state, state);
791 next = temp->next;
793 SAFE_FREE(temp);
794 temp = next;
798 /* Parse winbindd related parameters */
800 BOOL winbindd_param_init(void)
802 /* Parse winbind uid and winbind_gid parameters */
804 if (!lp_idmap_uid(&server_state.uid_low, &server_state.uid_high)) {
805 DEBUG(0, ("winbindd: idmap uid range missing or invalid\n"));
806 DEBUG(0, ("winbindd: cannot continue, exiting.\n"));
807 return False;
810 if (!lp_idmap_gid(&server_state.gid_low, &server_state.gid_high)) {
811 DEBUG(0, ("winbindd: idmap gid range missing or invalid\n"));
812 DEBUG(0, ("winbindd: cannot continue, exiting.\n"));
813 return False;
816 return True;
819 BOOL is_in_uid_range(uid_t uid)
821 return ((uid >= server_state.uid_low) &&
822 (uid <= server_state.uid_high));
825 BOOL is_in_gid_range(gid_t gid)
827 return ((gid >= server_state.gid_low) &&
828 (gid <= server_state.gid_high));
831 /* Is this a domain which we may assume no DOMAIN\ prefix? */
833 static BOOL assume_domain(const char *domain)
835 /* never assume the domain on a standalone server */
837 if ( lp_server_role() == ROLE_STANDALONE )
838 return False;
840 /* domain member servers may possibly assume for the domain name */
842 if ( lp_server_role() == ROLE_DOMAIN_MEMBER ) {
843 if ( !strequal(lp_workgroup(), domain) )
844 return False;
846 if ( lp_winbind_use_default_domain() || lp_winbind_trusted_domains_only() )
847 return True;
850 /* only left with a domain controller */
852 if ( strequal(get_global_sam_name(), domain) ) {
853 return True;
856 return False;
859 /* Parse a string of the form DOMAIN\user into a domain and a user */
861 BOOL parse_domain_user(const char *domuser, fstring domain, fstring user)
863 char *p = strchr(domuser,*lp_winbind_separator());
865 if ( !p ) {
866 fstrcpy(user, domuser);
868 if ( assume_domain(lp_workgroup())) {
869 fstrcpy(domain, lp_workgroup());
870 } else {
871 return False;
873 } else {
874 fstrcpy(user, p+1);
875 fstrcpy(domain, domuser);
876 domain[PTR_DIFF(p, domuser)] = 0;
879 strupper_m(domain);
881 return True;
884 BOOL parse_domain_user_talloc(TALLOC_CTX *mem_ctx, const char *domuser,
885 char **domain, char **user)
887 fstring fstr_domain, fstr_user;
888 if (!parse_domain_user(domuser, fstr_domain, fstr_user)) {
889 return False;
891 *domain = talloc_strdup(mem_ctx, fstr_domain);
892 *user = talloc_strdup(mem_ctx, fstr_user);
893 return ((*domain != NULL) && (*user != NULL));
896 /* Ensure an incoming username from NSS is fully qualified. Replace the
897 incoming fstring with DOMAIN <separator> user. Returns the same
898 values as parse_domain_user() but also replaces the incoming username.
899 Used to ensure all names are fully qualified within winbindd.
900 Used by the NSS protocols of auth, chauthtok, logoff and ccache_ntlm_auth.
901 The protocol definitions of auth_crap, chng_pswd_auth_crap
902 really should be changed to use this instead of doing things
903 by hand. JRA. */
905 BOOL canonicalize_username(fstring username_inout, fstring domain, fstring user)
907 if (!parse_domain_user(username_inout, domain, user)) {
908 return False;
910 slprintf(username_inout, sizeof(fstring) - 1, "%s%c%s",
911 domain, *lp_winbind_separator(),
912 user);
913 return True;
917 Fill DOMAIN\\USERNAME entry accounting 'winbind use default domain' and
918 'winbind separator' options.
919 This means:
920 - omit DOMAIN when 'winbind use default domain = true' and DOMAIN is
921 lp_workgroup()
923 If we are a PDC or BDC, and this is for our domain, do likewise.
925 Also, if omit DOMAIN if 'winbind trusted domains only = true', as the
926 username is then unqualified in unix
928 We always canonicalize as UPPERCASE DOMAIN, lowercase username.
930 void fill_domain_username(fstring name, const char *domain, const char *user, BOOL can_assume)
932 fstring tmp_user;
934 fstrcpy(tmp_user, user);
935 strlower_m(tmp_user);
937 if (can_assume && assume_domain(domain)) {
938 strlcpy(name, tmp_user, sizeof(fstring));
939 } else {
940 slprintf(name, sizeof(fstring) - 1, "%s%c%s",
941 domain, *lp_winbind_separator(),
942 tmp_user);
947 * Winbindd socket accessor functions
950 char *get_winbind_priv_pipe_dir(void)
952 return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
955 /* Open the winbindd socket */
957 static int _winbindd_socket = -1;
958 static int _winbindd_priv_socket = -1;
960 int open_winbindd_socket(void)
962 if (_winbindd_socket == -1) {
963 _winbindd_socket = create_pipe_sock(
964 WINBINDD_SOCKET_DIR, WINBINDD_SOCKET_NAME, 0755);
965 DEBUG(10, ("open_winbindd_socket: opened socket fd %d\n",
966 _winbindd_socket));
969 return _winbindd_socket;
972 int open_winbindd_priv_socket(void)
974 if (_winbindd_priv_socket == -1) {
975 _winbindd_priv_socket = create_pipe_sock(
976 get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750);
977 DEBUG(10, ("open_winbindd_priv_socket: opened socket fd %d\n",
978 _winbindd_priv_socket));
981 return _winbindd_priv_socket;
984 /* Close the winbindd socket */
986 void close_winbindd_socket(void)
988 if (_winbindd_socket != -1) {
989 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
990 _winbindd_socket));
991 close(_winbindd_socket);
992 _winbindd_socket = -1;
994 if (_winbindd_priv_socket != -1) {
995 DEBUG(10, ("close_winbindd_socket: closing socket fd %d\n",
996 _winbindd_priv_socket));
997 close(_winbindd_priv_socket);
998 _winbindd_priv_socket = -1;
1003 * Client list accessor functions
1006 static struct winbindd_cli_state *_client_list;
1007 static int _num_clients;
1009 /* Return list of all connected clients */
1011 struct winbindd_cli_state *winbindd_client_list(void)
1013 return _client_list;
1016 /* Add a connection to the list */
1018 void winbindd_add_client(struct winbindd_cli_state *cli)
1020 DLIST_ADD(_client_list, cli);
1021 _num_clients++;
1024 /* Remove a client from the list */
1026 void winbindd_remove_client(struct winbindd_cli_state *cli)
1028 DLIST_REMOVE(_client_list, cli);
1029 _num_clients--;
1032 /* Close all open clients */
1034 void winbindd_kill_all_clients(void)
1036 struct winbindd_cli_state *cl = winbindd_client_list();
1038 DEBUG(10, ("winbindd_kill_all_clients: going postal\n"));
1040 while (cl) {
1041 struct winbindd_cli_state *next;
1043 next = cl->next;
1044 winbindd_remove_client(cl);
1045 cl = next;
1049 /* Return number of open clients */
1051 int winbindd_num_clients(void)
1053 return _num_clients;
1056 /*****************************************************************************
1057 For idmap conversion: convert one record to new format
1058 Ancient versions (eg 2.2.3a) of winbindd_idmap.tdb mapped DOMAINNAME/rid
1059 instead of the SID.
1060 *****************************************************************************/
1061 static int convert_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, void *state)
1063 struct winbindd_domain *domain;
1064 char *p;
1065 DOM_SID sid;
1066 uint32 rid;
1067 fstring keystr;
1068 fstring dom_name;
1069 TDB_DATA key2;
1070 BOOL *failed = (BOOL *)state;
1072 DEBUG(10,("Converting %s\n", key.dptr));
1074 p = strchr(key.dptr, '/');
1075 if (!p)
1076 return 0;
1078 *p = 0;
1079 fstrcpy(dom_name, key.dptr);
1080 *p++ = '/';
1082 domain = find_domain_from_name(dom_name);
1083 if (domain == NULL) {
1084 /* We must delete the old record. */
1085 DEBUG(0,("Unable to find domain %s\n", dom_name ));
1086 DEBUG(0,("deleting record %s\n", key.dptr ));
1088 if (tdb_delete(tdb, key) != 0) {
1089 DEBUG(0, ("Unable to delete record %s\n", key.dptr));
1090 *failed = True;
1091 return -1;
1094 return 0;
1097 rid = atoi(p);
1099 sid_copy(&sid, &domain->sid);
1100 sid_append_rid(&sid, rid);
1102 sid_to_string(keystr, &sid);
1103 key2.dptr = keystr;
1104 key2.dsize = strlen(keystr) + 1;
1106 if (tdb_store(tdb, key2, data, TDB_INSERT) != 0) {
1107 DEBUG(0,("Unable to add record %s\n", key2.dptr ));
1108 *failed = True;
1109 return -1;
1112 if (tdb_store(tdb, data, key2, TDB_REPLACE) != 0) {
1113 DEBUG(0,("Unable to update record %s\n", data.dptr ));
1114 *failed = True;
1115 return -1;
1118 if (tdb_delete(tdb, key) != 0) {
1119 DEBUG(0,("Unable to delete record %s\n", key.dptr ));
1120 *failed = True;
1121 return -1;
1124 return 0;
1127 /* These definitions are from sam/idmap_tdb.c. Replicated here just
1128 out of laziness.... :-( */
1130 /* High water mark keys */
1131 #define HWM_GROUP "GROUP HWM"
1132 #define HWM_USER "USER HWM"
1134 /*****************************************************************************
1135 Convert the idmap database from an older version.
1136 *****************************************************************************/
1138 static BOOL idmap_convert(const char *idmap_name)
1140 int32 vers;
1141 BOOL bigendianheader;
1142 BOOL failed = False;
1143 TDB_CONTEXT *idmap_tdb;
1145 if (!(idmap_tdb = tdb_open_log(idmap_name, 0,
1146 TDB_DEFAULT, O_RDWR,
1147 0600))) {
1148 DEBUG(0, ("idmap_convert: Unable to open idmap database\n"));
1149 return False;
1152 bigendianheader = (tdb_get_flags(idmap_tdb) & TDB_BIGENDIAN) ? True : False;
1154 vers = tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION");
1156 if (((vers == -1) && bigendianheader) || (IREV(vers) == IDMAP_VERSION)) {
1157 /* Arrggghh ! Bytereversed or old big-endian - make order independent ! */
1159 * high and low records were created on a
1160 * big endian machine and will need byte-reversing.
1163 int32 wm;
1165 wm = tdb_fetch_int32(idmap_tdb, HWM_USER);
1167 if (wm != -1) {
1168 wm = IREV(wm);
1169 } else {
1170 wm = server_state.uid_low;
1173 if (tdb_store_int32(idmap_tdb, HWM_USER, wm) == -1) {
1174 DEBUG(0, ("idmap_convert: Unable to byteswap user hwm in idmap database\n"));
1175 tdb_close(idmap_tdb);
1176 return False;
1179 wm = tdb_fetch_int32(idmap_tdb, HWM_GROUP);
1180 if (wm != -1) {
1181 wm = IREV(wm);
1182 } else {
1183 wm = server_state.gid_low;
1186 if (tdb_store_int32(idmap_tdb, HWM_GROUP, wm) == -1) {
1187 DEBUG(0, ("idmap_convert: Unable to byteswap group hwm in idmap database\n"));
1188 tdb_close(idmap_tdb);
1189 return False;
1193 /* the old format stored as DOMAIN/rid - now we store the SID direct */
1194 tdb_traverse(idmap_tdb, convert_fn, &failed);
1196 if (failed) {
1197 DEBUG(0, ("Problem during conversion\n"));
1198 tdb_close(idmap_tdb);
1199 return False;
1202 if (tdb_store_int32(idmap_tdb, "IDMAP_VERSION", IDMAP_VERSION) == -1) {
1203 DEBUG(0, ("idmap_convert: Unable to dtore idmap version in databse\n"));
1204 tdb_close(idmap_tdb);
1205 return False;
1208 tdb_close(idmap_tdb);
1209 return True;
1212 /*****************************************************************************
1213 Convert the idmap database from an older version if necessary
1214 *****************************************************************************/
1216 BOOL winbindd_upgrade_idmap(void)
1218 pstring idmap_name;
1219 pstring backup_name;
1220 SMB_STRUCT_STAT stbuf;
1221 TDB_CONTEXT *idmap_tdb;
1223 pstrcpy(idmap_name, lock_path("winbindd_idmap.tdb"));
1225 if (!file_exist(idmap_name, &stbuf)) {
1226 /* nothing to convert return */
1227 return True;
1230 if (!(idmap_tdb = tdb_open_log(idmap_name, 0,
1231 TDB_DEFAULT, O_RDWR,
1232 0600))) {
1233 DEBUG(0, ("idmap_convert: Unable to open idmap database\n"));
1234 return False;
1237 if (tdb_fetch_int32(idmap_tdb, "IDMAP_VERSION") == IDMAP_VERSION) {
1238 /* nothing to convert return */
1239 tdb_close(idmap_tdb);
1240 return True;
1243 /* backup_tdb expects the tdb not to be open */
1244 tdb_close(idmap_tdb);
1246 DEBUG(0, ("Upgrading winbindd_idmap.tdb from an old version\n"));
1248 pstrcpy(backup_name, idmap_name);
1249 pstrcat(backup_name, ".bak");
1251 if (backup_tdb(idmap_name, backup_name, 0) != 0) {
1252 DEBUG(0, ("Could not backup idmap database\n"));
1253 return False;
1256 return idmap_convert(idmap_name);
1259 NTSTATUS lookup_usergroups_cached(struct winbindd_domain *domain,
1260 TALLOC_CTX *mem_ctx,
1261 const DOM_SID *user_sid,
1262 uint32 *p_num_groups, DOM_SID **user_sids)
1264 NET_USER_INFO_3 *info3 = NULL;
1265 NTSTATUS status = NT_STATUS_NO_MEMORY;
1266 int i;
1267 size_t num_groups = 0;
1268 DOM_SID group_sid, primary_group;
1270 DEBUG(3,(": lookup_usergroups_cached\n"));
1272 *user_sids = NULL;
1273 num_groups = 0;
1274 *p_num_groups = 0;
1276 info3 = netsamlogon_cache_get(mem_ctx, user_sid);
1278 if (info3 == NULL) {
1279 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1282 if (info3->num_groups == 0) {
1283 SAFE_FREE(info3);
1284 return NT_STATUS_UNSUCCESSFUL;
1287 /* always add the primary group to the sid array */
1288 sid_compose(&primary_group, &info3->dom_sid.sid, info3->user_rid);
1290 if (!add_sid_to_array(mem_ctx, &primary_group, user_sids, &num_groups)) {
1291 SAFE_FREE(info3);
1292 return NT_STATUS_NO_MEMORY;
1295 for (i=0; i<info3->num_groups; i++) {
1296 sid_copy(&group_sid, &info3->dom_sid.sid);
1297 sid_append_rid(&group_sid, info3->gids[i].g_rid);
1299 if (!add_sid_to_array(mem_ctx, &group_sid, user_sids,
1300 &num_groups)) {
1301 SAFE_FREE(info3);
1302 return NT_STATUS_NO_MEMORY;
1306 SAFE_FREE(info3);
1307 *p_num_groups = num_groups;
1308 status = (user_sids != NULL) ? NT_STATUS_OK : NT_STATUS_NO_MEMORY;
1310 DEBUG(3,(": lookup_usergroups_cached succeeded\n"));
1312 return status;
1315 /*********************************************************************
1316 We use this to remove spaces from user and group names
1317 ********************************************************************/
1319 void ws_name_replace( char *name, char replace )
1321 char replace_char[2] = { 0x0, 0x0 };
1323 if ( !lp_winbind_normalize_names() || (replace == '\0') )
1324 return;
1326 replace_char[0] = replace;
1327 all_string_sub( name, " ", replace_char, 0 );
1329 return;
1332 /*********************************************************************
1333 We use this to do the inverse of ws_name_replace()
1334 ********************************************************************/
1336 void ws_name_return( char *name, char replace )
1338 char replace_char[2] = { 0x0, 0x0 };
1340 if ( !lp_winbind_normalize_names() || (replace == '\0') )
1341 return;
1343 replace_char[0] = replace;
1344 all_string_sub( name, replace_char, " ", 0 );
1346 return;