2 Unix SMB/CIFS implementation.
4 Winbind rpc backend functions
6 Copyright (C) Tim Potter 2000-2001
7 Copyright (C) Andrew Tridgell 2001
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.
26 /*******************************************************************
27 Duplicate a UNISTR2 string into a UNIX codepage null terminated char*
28 using a talloc context
29 ********************************************************************/
31 static char *unistr2_tdup(TALLOC_CTX
*ctx
, const UNISTR2
*str
)
34 int maxlen
= (str
->uni_str_len
+1)*4;
37 s
= (char *)talloc(ctx
, maxlen
); /* convervative */
40 unistr2_to_unix(s
, str
, maxlen
);
44 /* Query display info for a domain. This returns enough information plus a
45 bit extra to give an overview of domain users for the User Manager
47 static NTSTATUS
query_user_list(struct winbindd_domain
*domain
,
50 WINBIND_USERINFO
**info
)
55 BOOL got_dom_pol
= False
;
56 uint32 des_access
= SEC_RIGHTS_MAXIMUM_ALLOWED
;
57 int i
, loop_count
= 0;
67 if (!NT_STATUS_IS_OK(result
= cm_get_sam_handle(domain
->name
, &hnd
)))
70 /* Get domain handle */
72 result
= cli_samr_open_domain(hnd
->cli
, mem_ctx
, &hnd
->pol
,
73 des_access
, &domain
->sid
, &dom_pol
);
74 } while (!NT_STATUS_IS_OK(result
) && (retry
++ < 1) &&
75 hnd
&& hnd
->cli
&& hnd
->cli
->fd
== -1);
77 if (!NT_STATUS_IS_OK(result
))
86 uint32 count
= 0, start
=i
, max_entries
, max_size
;
90 ctr
.sam
.info1
= &info1
;
92 ctx2
= talloc_init_named("winbindd dispinfo");
94 result
= NT_STATUS_NO_MEMORY
;
98 get_query_dispinfo_params(
99 loop_count
, &max_entries
, &max_size
);
101 /* Query display info level 1 */
102 result
= cli_samr_query_dispinfo(
103 hnd
->cli
, ctx2
, &dom_pol
, &start
, 1, &count
,
104 max_entries
, max_size
, &ctr
);
108 if (!NT_STATUS_IS_OK(result
) &&
109 !NT_STATUS_EQUAL(result
, STATUS_MORE_ENTRIES
)) break;
111 (*num_entries
) += count
;
113 /* now map the result into the WINBIND_USERINFO structure */
114 (*info
) = talloc_realloc(mem_ctx
, *info
,
115 (*num_entries
)*sizeof(WINBIND_USERINFO
));
117 result
= NT_STATUS_NO_MEMORY
;
118 talloc_destroy(ctx2
);
122 for (j
=0;j
<count
;i
++, j
++) {
123 /* unistr2_tdup converts to UNIX charset. */
124 (*info
)[i
].acct_name
= unistr2_tdup(mem_ctx
, &info1
.str
[j
].uni_acct_name
);
125 (*info
)[i
].full_name
= unistr2_tdup(mem_ctx
, &info1
.str
[j
].uni_full_name
);
126 (*info
)[i
].user_rid
= info1
.sam
[j
].rid_user
;
127 /* For the moment we set the primary group for
128 every user to be the Domain Users group.
129 There are serious problems with determining
130 the actual primary group for large domains.
131 This should really be made into a 'winbind
132 force group' smb.conf parameter or
133 something like that. */
134 (*info
)[i
].group_rid
= DOMAIN_GROUP_RID_USERS
;
137 talloc_destroy(ctx2
);
138 } while (NT_STATUS_EQUAL(result
, STATUS_MORE_ENTRIES
));
143 cli_samr_close(hnd
->cli
, mem_ctx
, &dom_pol
);
148 /* List all domain groups */
150 static NTSTATUS
enum_dom_groups(struct winbindd_domain
*domain
,
153 struct acct_info
**info
)
155 uint32 des_access
= SEC_RIGHTS_MAXIMUM_ALLOWED
;
167 if (!NT_STATUS_IS_OK(result
= cm_get_sam_handle(domain
->name
, &hnd
)))
170 result
= cli_samr_open_domain(hnd
->cli
, mem_ctx
,
171 &hnd
->pol
, des_access
, &domain
->sid
, &dom_pol
);
172 } while (!NT_STATUS_IS_OK(result
) && (retry
++ < 1) &&
173 hnd
&& hnd
->cli
&& hnd
->cli
->fd
== -1);
175 if (!NT_STATUS_IS_OK(result
))
179 struct acct_info
*info2
= NULL
;
181 TALLOC_CTX
*mem_ctx2
;
183 mem_ctx2
= talloc_init_named("enum_dom_groups[rpc]");
185 /* This call updates 'start' */
186 result
= cli_samr_enum_dom_groups(
187 hnd
->cli
, mem_ctx2
, &dom_pol
, &start
,
188 0xFFFF, /* buffer size? */ &info2
, &count
);
190 if (!NT_STATUS_IS_OK(result
) &&
191 !NT_STATUS_EQUAL(result
, STATUS_MORE_ENTRIES
)) {
192 talloc_destroy(mem_ctx2
);
196 (*info
) = talloc_realloc(mem_ctx
, *info
,
197 sizeof(**info
) * ((*num_entries
) + count
));
199 talloc_destroy(mem_ctx2
);
200 cli_samr_close(hnd
->cli
, mem_ctx
, &dom_pol
);
201 return NT_STATUS_NO_MEMORY
;
204 memcpy(&(*info
)[*num_entries
], info2
, count
*sizeof(*info2
));
205 (*num_entries
) += count
;
206 talloc_destroy(mem_ctx2
);
207 } while (NT_STATUS_EQUAL(result
, STATUS_MORE_ENTRIES
));
209 cli_samr_close(hnd
->cli
, mem_ctx
, &dom_pol
);
214 /* convert a single name to a sid in a domain */
215 static NTSTATUS
name_to_sid(struct winbindd_domain
*domain
,
218 enum SID_NAME_USE
*type
)
223 DOM_SID
*sids
= NULL
;
224 uint32
*types
= NULL
;
225 const char *full_name
;
228 if (!(mem_ctx
= talloc_init_named("name_to_sid[rpc] for [%s]\\[%s]", domain
->name
, name
))) {
229 DEBUG(0, ("talloc_init failed!\n"));
230 return NT_STATUS_NO_MEMORY
;
233 full_name
= talloc_asprintf(mem_ctx
, "%s\\%s", domain
->name
, name
);
236 DEBUG(0, ("talloc_asprintf failed!\n"));
237 talloc_destroy(mem_ctx
);
238 return NT_STATUS_NO_MEMORY
;
243 if (!NT_STATUS_IS_OK(result
= cm_get_lsa_handle(domain
->name
, &hnd
))) {
244 talloc_destroy(mem_ctx
);
245 return NT_STATUS_UNSUCCESSFUL
;
248 result
= cli_lsa_lookup_names(hnd
->cli
, mem_ctx
, &hnd
->pol
, 1,
249 &full_name
, &sids
, &types
);
250 } while (!NT_STATUS_IS_OK(result
) && (retry
++ < 1) &&
251 hnd
&& hnd
->cli
&& hnd
->cli
->fd
== -1);
253 /* Return rid and type if lookup successful */
255 if (NT_STATUS_IS_OK(result
)) {
256 sid_copy(sid
, &sids
[0]);
260 talloc_destroy(mem_ctx
);
265 convert a domain SID to a user or group name
267 static NTSTATUS
sid_to_name(struct winbindd_domain
*domain
,
271 enum SID_NAME_USE
*type
)
282 if (!NT_STATUS_IS_OK(result
= cm_get_lsa_handle(domain
->name
, &hnd
)))
283 return NT_STATUS_UNSUCCESSFUL
;
285 result
= cli_lsa_lookup_sids(hnd
->cli
, mem_ctx
, &hnd
->pol
,
286 1, sid
, &domains
, &names
, &types
);
287 } while (!NT_STATUS_IS_OK(result
) && (retry
++ < 1) &&
288 hnd
&& hnd
->cli
&& hnd
->cli
->fd
== -1);
290 if (NT_STATUS_IS_OK(result
)) {
293 DEBUG(5,("Mapped sid to [%s]\\[%s]\n", domains
[0], *name
));
296 if (strcasecmp(domain
->name
, domains
[0]) != 0) {
297 DEBUG(1, ("domain name from domain param and PDC lookup return differ! (%s vs %s)\n", domain
->name
, domains
[0]));
298 return NT_STATUS_UNSUCCESSFUL
;
305 /* Lookup user information from a rid or username. */
306 static NTSTATUS
query_user(struct winbindd_domain
*domain
,
309 WINBIND_USERINFO
*user_info
)
313 POLICY_HND dom_pol
, user_pol
;
314 BOOL got_dom_pol
= False
, got_user_pol
= False
;
315 SAM_USERINFO_CTR
*ctr
;
321 if (!NT_STATUS_IS_OK(result
= cm_get_sam_handle(domain
->name
, &hnd
)))
324 /* Get domain handle */
325 result
= cli_samr_open_domain(hnd
->cli
, mem_ctx
, &hnd
->pol
,
326 SEC_RIGHTS_MAXIMUM_ALLOWED
,
327 &domain
->sid
, &dom_pol
);
328 } while (!NT_STATUS_IS_OK(result
) && (retry
++ < 1) &&
329 hnd
&& hnd
->cli
&& hnd
->cli
->fd
== -1);
331 if (!NT_STATUS_IS_OK(result
))
336 /* Get user handle */
337 result
= cli_samr_open_user(hnd
->cli
, mem_ctx
, &dom_pol
,
338 SEC_RIGHTS_MAXIMUM_ALLOWED
, user_rid
, &user_pol
);
340 if (!NT_STATUS_IS_OK(result
))
346 result
= cli_samr_query_userinfo(hnd
->cli
, mem_ctx
, &user_pol
,
349 if (!NT_STATUS_IS_OK(result
))
352 cli_samr_close(hnd
->cli
, mem_ctx
, &user_pol
);
353 got_user_pol
= False
;
355 user_info
->user_rid
= user_rid
;
356 user_info
->group_rid
= ctr
->info
.id21
->group_rid
;
357 user_info
->acct_name
= unistr2_tdup(mem_ctx
,
358 &ctr
->info
.id21
->uni_user_name
);
359 user_info
->full_name
= unistr2_tdup(mem_ctx
,
360 &ctr
->info
.id21
->uni_full_name
);
363 /* Clean up policy handles */
365 cli_samr_close(hnd
->cli
, mem_ctx
, &user_pol
);
368 cli_samr_close(hnd
->cli
, mem_ctx
, &dom_pol
);
373 /* Lookup groups a user is a member of. I wish Unix had a call like this! */
374 static NTSTATUS
lookup_usergroups(struct winbindd_domain
*domain
,
377 uint32
*num_groups
, uint32
**user_gids
)
381 POLICY_HND dom_pol
, user_pol
;
382 uint32 des_access
= SEC_RIGHTS_MAXIMUM_ALLOWED
;
383 BOOL got_dom_pol
= False
, got_user_pol
= False
;
384 DOM_GID
*user_groups
;
394 if (!NT_STATUS_IS_OK(result
= cm_get_sam_handle(domain
->name
, &hnd
)))
397 /* Get domain handle */
398 result
= cli_samr_open_domain(hnd
->cli
, mem_ctx
, &hnd
->pol
,
399 des_access
, &domain
->sid
, &dom_pol
);
400 } while (!NT_STATUS_IS_OK(result
) && (retry
++ < 1) &&
401 hnd
&& hnd
->cli
&& hnd
->cli
->fd
== -1);
403 if (!NT_STATUS_IS_OK(result
))
408 /* Get user handle */
409 result
= cli_samr_open_user(hnd
->cli
, mem_ctx
, &dom_pol
,
410 des_access
, user_rid
, &user_pol
);
412 if (!NT_STATUS_IS_OK(result
))
417 /* Query user rids */
418 result
= cli_samr_query_usergroups(hnd
->cli
, mem_ctx
, &user_pol
,
419 num_groups
, &user_groups
);
421 if (!NT_STATUS_IS_OK(result
) || (*num_groups
) == 0)
424 (*user_gids
) = talloc(mem_ctx
, sizeof(uint32
) * (*num_groups
));
425 for (i
=0;i
<(*num_groups
);i
++) {
426 (*user_gids
)[i
] = user_groups
[i
].g_rid
;
430 /* Clean up policy handles */
432 cli_samr_close(hnd
->cli
, mem_ctx
, &user_pol
);
435 cli_samr_close(hnd
->cli
, mem_ctx
, &dom_pol
);
441 /* Lookup group membership given a rid. */
442 static NTSTATUS
lookup_groupmem(struct winbindd_domain
*domain
,
444 uint32 group_rid
, uint32
*num_names
,
445 uint32
**rid_mem
, char ***names
,
450 uint32 i
, total_names
= 0;
451 POLICY_HND dom_pol
, group_pol
;
452 uint32 des_access
= SEC_RIGHTS_MAXIMUM_ALLOWED
;
453 BOOL got_dom_pol
= False
, got_group_pol
= False
;
461 if (!NT_STATUS_IS_OK(result
= cm_get_sam_handle(domain
->name
, &hnd
)))
464 /* Get domain handle */
465 result
= cli_samr_open_domain(hnd
->cli
, mem_ctx
, &hnd
->pol
,
466 des_access
, &domain
->sid
, &dom_pol
);
467 } while (!NT_STATUS_IS_OK(result
) && (retry
++ < 1) &&
468 hnd
&& hnd
->cli
&& hnd
->cli
->fd
== -1);
470 if (!NT_STATUS_IS_OK(result
))
475 /* Get group handle */
477 result
= cli_samr_open_group(hnd
->cli
, mem_ctx
, &dom_pol
,
478 des_access
, group_rid
, &group_pol
);
480 if (!NT_STATUS_IS_OK(result
))
483 got_group_pol
= True
;
485 /* Step #1: Get a list of user rids that are the members of the
488 result
= cli_samr_query_groupmem(hnd
->cli
, mem_ctx
,
489 &group_pol
, num_names
, rid_mem
,
492 if (!NT_STATUS_IS_OK(result
))
495 /* Step #2: Convert list of rids into list of usernames. Do this
496 in bunches of ~1000 to avoid crashing NT4. It looks like there
497 is a buffer overflow or something like that lurking around
500 #define MAX_LOOKUP_RIDS 900
502 *names
= talloc_zero(mem_ctx
, *num_names
* sizeof(char *));
503 *name_types
= talloc_zero(mem_ctx
, *num_names
* sizeof(uint32
));
505 for (i
= 0; i
< *num_names
; i
+= MAX_LOOKUP_RIDS
) {
506 int num_lookup_rids
= MIN(*num_names
- i
, MAX_LOOKUP_RIDS
);
507 uint32 tmp_num_names
= 0;
508 char **tmp_names
= NULL
;
509 uint32
*tmp_types
= NULL
;
511 /* Lookup a chunk of rids */
513 result
= cli_samr_lookup_rids(hnd
->cli
, mem_ctx
,
514 &dom_pol
, 1000, /* flags */
518 &tmp_names
, &tmp_types
);
520 if (!NT_STATUS_IS_OK(result
))
523 /* Copy result into array. The talloc system will take
524 care of freeing the temporary arrays later on. */
526 memcpy(&(*names
)[i
], tmp_names
, sizeof(char *) *
529 memcpy(&(*name_types
)[i
], tmp_types
, sizeof(uint32
) *
532 total_names
+= tmp_num_names
;
535 *num_names
= total_names
;
539 cli_samr_close(hnd
->cli
, mem_ctx
, &group_pol
);
542 cli_samr_close(hnd
->cli
, mem_ctx
, &dom_pol
);
547 /* find the sequence number for a domain */
549 #ifdef WITH_HORRIBLE_LDAP_NATIVE_MODE_HACK
552 static SIG_ATOMIC_T gotalarm
;
554 /***************************************************************
555 Signal function to tell us we timed out.
556 ****************************************************************/
558 static void gotalarm_sig(void)
563 static LDAP
*ldap_open_with_timeout(const char *server
, int port
, unsigned int to
)
569 CatchSignal(SIGALRM
, SIGNAL_CAST gotalarm_sig
);
571 /* End setup timeout. */
573 ldp
= ldap_open(server
, port
);
575 /* Teardown timeout. */
576 CatchSignal(SIGALRM
, SIGNAL_CAST SIG_IGN
);
582 int get_ldap_seq(const char *server
, uint32
*seq
)
586 char *attrs
[] = {"highestCommittedUSN", NULL
};
587 LDAPMessage
*res
= NULL
;
588 char **values
= NULL
;
591 *seq
= DOM_SEQUENCE_NONE
;
594 * 10 second timeout on open. This is needed as the search timeout
595 * doesn't seem to apply to doing an open as well. JRA.
598 if ((ldp
= ldap_open_with_timeout(server
, LDAP_PORT
, 10)) == NULL
)
602 /* As per tridge comment this doesn't seem to be needed. JRA */
603 if ((err
= ldap_simple_bind_s(ldp
, NULL
, NULL
)) != 0)
607 /* Timeout if no response within 20 seconds. */
611 if (ldap_search_st(ldp
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", &attrs
[0], 0, &to
, &res
))
614 if (ldap_count_entries(ldp
, res
) != 1)
617 values
= ldap_get_values(ldp
, res
, "highestCommittedUSN");
618 if (!values
|| !values
[0])
621 *seq
= atoi(values
[0]);
627 ldap_value_free(values
);
634 #endif /* WITH_HORRIBLE_LDAP_NATIVE_MODE_HACK */
636 static NTSTATUS
sequence_number(struct winbindd_domain
*domain
, uint32
*seq
)
641 uint16 switch_value
= 2;
643 uint32 seqnum
= DOM_SEQUENCE_NONE
;
645 BOOL got_dom_pol
= False
;
646 uint32 des_access
= SEC_RIGHTS_MAXIMUM_ALLOWED
;
649 *seq
= DOM_SEQUENCE_NONE
;
651 if (!(mem_ctx
= talloc_init_named("sequence_number[rpc]")))
652 return NT_STATUS_NO_MEMORY
;
657 if (!NT_STATUS_IS_OK(result
= cm_get_sam_handle(domain
->name
, &hnd
)))
660 #ifdef WITH_HORRIBLE_LDAP_NATIVE_MODE_HACK
661 if (get_ldap_seq( inet_ntoa(hnd
->cli
->dest_ip
), seq
) == 0) {
662 result
= NT_STATUS_OK
;
664 DEBUG(10,("domain_sequence_number: LDAP for domain %s is %u\n",
665 domain
->name
, (unsigned)seqnum
));
669 DEBUG(10,("domain_sequence_number: failed to get LDAP sequence number (%u) for domain %s\n",
670 (unsigned)seqnum
, domain
->name
));
672 #endif /* WITH_HORRIBLE_LDAP_NATIVE_MODE_HACK */
674 /* Get domain handle */
675 result
= cli_samr_open_domain(hnd
->cli
, mem_ctx
, &hnd
->pol
,
676 des_access
, &domain
->sid
, &dom_pol
);
677 } while (!NT_STATUS_IS_OK(result
) && (retry
++ < 1) &&
678 hnd
&& hnd
->cli
&& hnd
->cli
->fd
== -1);
680 if (!NT_STATUS_IS_OK(result
))
685 /* Query domain info */
687 result
= cli_samr_query_dom_info(hnd
->cli
, mem_ctx
, &dom_pol
,
690 if (NT_STATUS_IS_OK(result
)) {
691 seqnum
= ctr
.info
.inf2
.seq_num
;
692 seqnum
+= ctr
.info
.inf2
.num_domain_usrs
;
693 seqnum
+= ctr
.info
.inf2
.num_domain_grps
;
694 seqnum
+= ctr
.info
.inf2
.num_local_grps
;
695 DEBUG(10,("domain_sequence_number: for domain %s is %u\n", domain
->name
, (unsigned)seqnum
));
697 DEBUG(10,("domain_sequence_number: failed to get sequence number (%u) for domain %s\n",
698 (unsigned)seqnum
, domain
->name
));
704 cli_samr_close(hnd
->cli
, mem_ctx
, &dom_pol
);
706 talloc_destroy(mem_ctx
);
713 /* get a list of trusted domains */
714 static NTSTATUS
trusted_domains(struct winbindd_domain
*domain
,
729 if (!NT_STATUS_IS_OK(result
= cm_get_lsa_handle(lp_workgroup(), &hnd
)))
732 result
= cli_lsa_enum_trust_dom(hnd
->cli
, mem_ctx
,
733 &hnd
->pol
, &enum_ctx
, num_domains
,
735 } while (!NT_STATUS_IS_OK(result
) && (retry
++ < 1) &&
736 hnd
&& hnd
->cli
&& hnd
->cli
->fd
== -1);
741 /* find the domain sid for a domain */
742 static NTSTATUS
domain_sid(struct winbindd_domain
*domain
, DOM_SID
*sid
)
750 if (!(mem_ctx
= talloc_init_named("domain_sid[rpc]")))
751 return NT_STATUS_NO_MEMORY
;
756 if (!NT_STATUS_IS_OK(result
= cm_get_lsa_handle(domain
->name
, &hnd
)))
759 result
= cli_lsa_query_info_policy(hnd
->cli
, mem_ctx
,
760 &hnd
->pol
, 0x05, level5_dom
, sid
);
762 } while (!NT_STATUS_IS_OK(result
) && (retry
++ < 1) &&
763 hnd
&& hnd
->cli
&& hnd
->cli
->fd
== -1);
765 talloc_destroy(mem_ctx
);
769 /* the rpc backend methods are exposed via this structure */
770 struct winbindd_methods msrpc_methods
= {