4 * Copyright (c) 2000 The Regents of the University of Michigan.
7 * Copyright (c) 2004 Andy Adamson <andros@UMICH.EDU>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
23 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
24 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 #include <sys/types.h>
38 #include <sys/socket.h>
48 /* We are using deprecated functions, get the prototypes... */
49 #define LDAP_DEPRECATED 1
52 #include "nfsidmap_internal.h"
55 /* attribute/objectclass default mappings */
56 #define DEFAULT_UMICH_OBJCLASS_REMOTE_PERSON "NFSv4RemotePerson"
57 #define DEFAULT_UMICH_OBJCLASS_REMOTE_GROUP "NFSv4RemoteGroup"
58 #define DEFAULT_UMICH_ATTR_NFSNAME "NFSv4Name"
59 #define DEFAULT_UMICH_ATTR_ACCTNAME "uid"
60 #define DEFAULT_UMICH_ATTR_UIDNUMBER "uidNumber"
61 #define DEFAULT_UMICH_ATTR_GROUP_NFSNAME "NFSv4Name"
62 #define DEFAULT_UMICH_ATTR_GIDNUMBER "gidNumber"
63 #define DEFAULT_UMICH_ATTR_MEMBERUID "memberUid"
64 #define DEFAULT_UMICH_ATTR_GSSAUTHNAME "GSSAuthName"
65 #define DEFAULT_UMICH_ATTR_MEMBEROF "memberof"
67 #define DEFAULT_UMICH_SEARCH_TIMEOUT 4
70 #define LDAP_SECTION "UMICH_SCHEMA"
72 #ifndef LDAP_FILT_MAXSIZ
73 #define LDAP_FILT_MAXSIZ 1024
77 /* Local structure definitions */
79 struct ldap_map_names
{
80 char *NFSv4_person_objcls
;
81 char *NFSv4_nfsname_attr
;
82 char *NFSv4_acctname_attr
;
84 char *NFSv4_group_objcls
;
85 char *NFSv4_group_nfsname_attr
;
87 char *NFSv4_member_attr
;
88 char *NFSv4_member_of_attr
;
89 char *GSS_principal_attr
;
90 char *NFSv4_grouplist_filter
; /* Filter for grouplist lookups */
93 struct umich_ldap_info
{
94 char *server
; /* server name/address */
95 int port
; /* server port */
96 char *base
; /* base DN */
97 char *people_tree
; /* base DN to start searches for people */
98 char *group_tree
; /* base DN to start searches for groups */
99 char *user_dn
; /* optional DN for user account when binding */
100 char *passwd
; /* Password to use when binding to directory */
101 int use_ssl
; /* SSL flag */
102 char *ca_cert
; /* File location of the ca_cert */
103 int memberof_for_groups
;/* Use 'memberof' attribute when
104 looking up user groups */
105 int ldap_timeout
; /* Timeout in seconds for searches
111 static struct umich_ldap_info ldap_info
= {
121 .memberof_for_groups
= 0,
122 .ldap_timeout
= DEFAULT_UMICH_SEARCH_TIMEOUT
,
125 static struct ldap_map_names ldap_map
= {
126 .NFSv4_person_objcls
= NULL
,
127 .NFSv4_nfsname_attr
= NULL
,
128 .NFSv4_uid_attr
= NULL
,
129 .NFSv4_acctname_attr
= NULL
,
130 .NFSv4_group_objcls
= NULL
,
131 .NFSv4_group_nfsname_attr
= NULL
,
132 .NFSv4_gid_attr
= NULL
,
133 .NFSv4_member_attr
= NULL
,
134 .NFSv4_member_of_attr
= NULL
,
135 .GSS_principal_attr
= NULL
,
136 .NFSv4_grouplist_filter
= NULL
,
142 ldap_init_and_bind(LDAP
**pld
,
144 struct umich_ldap_info
*linfo
)
149 int current_version
, new_version
;
150 char server_url
[1024];
151 int debug_level
= 65535;
153 LDAPAPIInfo apiinfo
= {.ldapai_info_version
= LDAP_API_INFO_VERSION
};
155 snprintf(server_url
, sizeof(server_url
), "%s://%s:%d",
156 (linfo
->use_ssl
&& linfo
->ca_cert
) ? "ldaps" : "ldap",
157 linfo
->server
, linfo
->port
);
160 * XXX We really, REALLY only want to initialize once, not for
161 * each request. Figure out how to do that!
163 if ((lerr
= ldap_initialize(&ld
, server_url
)) != LDAP_SUCCESS
) {
164 IDMAP_LOG(0, ("ldap_init_and_bind: ldap_initialize() failed "
165 "to [%s]: %s (%d)\n", server_url
,
166 ldap_err2string(lerr
), lerr
));
170 if ((ldap_set_option(ld
, LDAP_OPT_DEBUG_LEVEL
, &debug_level
)
172 IDMAP_LOG(0, ("ldap_init_and_bind: error setting ldap "
173 "library debugging level\n"));
178 * Get LDAP API information and compare the protocol version there
179 * to the protocol version returned directly from get_option.
181 ldap_get_option(ld
, LDAP_OPT_API_INFO
, &apiinfo
);
182 if (apiinfo
.ldapai_info_version
!= LDAP_API_INFO_VERSION
) {
183 IDMAP_LOG(0, ("ldap_init_and_bind: APIInfo version mismatch: "
184 "library %d, header %d\n",
185 apiinfo
.ldapai_info_version
, LDAP_API_INFO_VERSION
));
188 ldap_get_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, ¤t_version
);
189 if (apiinfo
.ldapai_protocol_version
== LDAP_VERSION3
&&
190 current_version
!= LDAP_VERSION3
) {
191 new_version
= LDAP_VERSION3
;
192 IDMAP_LOG(4, ("ldap_init_and_bind: version mismatch between "
193 "API information and protocol version. Setting "
194 "protocol version to %d\n", new_version
));
195 ldap_set_option(ld
, LDAP_OPT_PROTOCOL_VERSION
, &new_version
);
198 for (i
= 0; apiinfo
.ldapai_extensions
[i
]; i
++) {
199 char *extension
= apiinfo
.ldapai_extensions
[i
];
200 ldap_memfree (extension
);
202 ldap_memfree (apiinfo
.ldapai_extensions
);
203 ldap_memfree(apiinfo
.ldapai_vendor_name
);
205 /* Set sizelimit option if requested */
207 ldap_set_option(ld
, LDAP_OPT_SIZELIMIT
, (void *)sizelimit
);
210 /* Set option to to use SSL/TLS if requested */
211 if (linfo
->use_ssl
&& linfo
->ca_cert
) {
212 int tls_type
= LDAP_OPT_X_TLS_HARD
;
214 lerr
= ldap_set_option(ld
, LDAP_OPT_X_TLS
, &tls_type
);
215 if (lerr
!= LDAP_SUCCESS
) {
216 IDMAP_LOG(2, ("ldap_init_and_bind: setting SSL "
217 "failed : %s (%d)\n",
218 ldap_err2string(lerr
), lerr
));
221 lerr
= ldap_set_option(NULL
, LDAP_OPT_X_TLS_CACERTFILE
,
223 if (lerr
!= LDAP_SUCCESS
) {
224 IDMAP_LOG(2, ("ldap_init_and_bind: setting CA "
225 "certificate file failed : %s (%d)\n",
226 ldap_err2string(lerr
), lerr
));
231 /* If we have a DN (and password) attempt an authenticated bind */
232 if (linfo
->user_dn
) {
234 lerr
= ldap_simple_bind_s(ld
, linfo
->user_dn
, linfo
->passwd
);
237 if (lerr
== LDAP_PROTOCOL_ERROR
) {
238 ldap_get_option(ld
, LDAP_OPT_PROTOCOL_VERSION
,
240 new_version
= current_version
== LDAP_VERSION2
?
241 LDAP_VERSION3
: LDAP_VERSION2
;
242 ldap_set_option( ld
, LDAP_OPT_PROTOCOL_VERSION
,
244 IDMAP_LOG(2, ("ldap_init_and_bind: "
245 "got protocol error while attempting "
246 "bind with protocol version %d, "
247 "trying protocol version %d\n",
248 current_version
, new_version
));
249 if ((ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &errmsg
) == LDAP_SUCCESS
)
250 && (errmsg
!= NULL
) && (*errmsg
!= '\0')) {
251 IDMAP_LOG(2, ("ldap_init_and_bind: "
252 "Additional info: %s\n", errmsg
));
253 ldap_memfree(errmsg
);
257 IDMAP_LOG(2, ("ldap_init_and_bind: ldap_simple_bind_s "
258 "to [%s] as user '%s': %s (%d)\n",
259 server_url
, linfo
->user_dn
,
260 ldap_err2string(lerr
), lerr
));
261 if ((ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &errmsg
) == LDAP_SUCCESS
)
262 && (errmsg
!= NULL
)&& (*errmsg
!= '\0')) {
263 IDMAP_LOG(2, ("ldap_init_and_bind: "
264 "Additional info: %s\n", errmsg
));
265 ldap_memfree(errmsg
);
270 #ifdef LDAP_ANONYMOUS_BIND_REQUIRED
272 lerr
= ldap_simple_bind_s(ld
, NULL
, NULL
);
276 IDMAP_LOG(2, ("ldap_init_and_bind: ldap_simple_bind_s "
277 "to [%s] as anonymous: %s (%d)\n", server_url
,
278 ldap_err2string(lerr
), lerr
));
279 if ((ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &errmsg
) == LDAP_SUCCESS
)
280 && (errmsg
!= NULL
) && (*errmsg
!= '\0')) {
281 IDMAP_LOG(2, ("ldap_init_and_bind: "
282 "Additional info: %s\n", errmsg
));
283 ldap_memfree(errmsg
);
297 umich_name_to_ids(char *name
, int idtype
, uid_t
*uid
, gid_t
*gid
,
298 char *attrtype
, struct umich_ldap_info
*linfo
)
301 struct timeval timeout
= {
302 .tv_sec
= linfo
->ldap_timeout
,
304 LDAPMessage
*result
= NULL
, *entry
;
305 BerElement
*ber
= NULL
;
306 char **idstr
, filter
[LDAP_FILT_MAXSIZ
], *base
;
309 int count
= 0, err
, lerr
, f_len
;
313 if (uid
== NULL
|| gid
== NULL
|| name
== NULL
||
314 attrtype
== NULL
|| linfo
== NULL
|| linfo
->server
== NULL
||
315 linfo
->people_tree
== NULL
|| linfo
->group_tree
== NULL
)
321 if (idtype
== IDTYPE_USER
) {
322 if ((f_len
= snprintf(filter
, LDAP_FILT_MAXSIZ
,
323 "(&(objectClass=%s)(%s=%s))",
324 ldap_map
.NFSv4_person_objcls
,
326 == LDAP_FILT_MAXSIZ
) {
327 IDMAP_LOG(0, ("ERROR: umich_name_to_ids: filter "
331 base
= linfo
->people_tree
;
333 else if (idtype
== IDTYPE_GROUP
) {
334 if ((f_len
= snprintf(filter
, LDAP_FILT_MAXSIZ
,
335 "(&(objectClass=%s)(%s=%s))",
336 ldap_map
.NFSv4_group_objcls
,
338 == LDAP_FILT_MAXSIZ
) {
339 IDMAP_LOG(0, ("ERROR: umich_name_to_ids: filter "
343 base
= linfo
->group_tree
;
346 IDMAP_LOG(0, ("ERROR: umich_name_to_ids: invalid idtype (%d)\n",
351 if (ldap_init_and_bind(&ld
, &sizelimit
, linfo
))
354 attrs
[0] = ldap_map
.NFSv4_uid_attr
;
355 attrs
[1] = ldap_map
.NFSv4_gid_attr
;
358 err
= ldap_search_st(ld
, base
, LDAP_SCOPE_SUBTREE
,
359 filter
, (char **)attrs
,
360 0, &timeout
, &result
);
364 IDMAP_LOG(2, ("umich_name_to_ids: ldap_search_st for "
365 "base '%s', filter '%s': %s (%d)\n",
366 base
, filter
, ldap_err2string(err
), err
));
367 if ((ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &errmsg
) == LDAP_SUCCESS
)
368 && (errmsg
!= NULL
) && (*errmsg
!= '\0')) {
369 IDMAP_LOG(2, ("umich_name_to_ids: "
370 "Additional info: %s\n", errmsg
));
371 ldap_memfree(errmsg
);
378 count
= ldap_count_entries(ld
, result
);
383 if (!(entry
= ldap_first_entry(ld
, result
))) {
384 lerr
= ldap_result2error(ld
, result
, 0);
385 IDMAP_LOG(2, ("umich_name_to_ids: ldap_first_entry: "
386 "%s (%d)\n", ldap_err2string(lerr
), lerr
));
391 * Attributes come back in no particular order, so we need
392 * to check each one to see what it is before assigning values.
393 * XXX There must be a better way than comparing the
394 * name of each attribute?
396 for (attr_res
= ldap_first_attribute(ld
, result
, &ber
);
398 attr_res
= ldap_next_attribute(ld
, result
, ber
)) {
400 unsigned long tmp_u
, tmp_g
;
404 if ((idstr
= ldap_get_values(ld
, result
, attr_res
)) == NULL
) {
405 lerr
= ldap_result2error(ld
, result
, 0);
406 IDMAP_LOG(2, ("umich_name_to_ids: ldap_get_values: "
407 "%s (%d)\n", ldap_err2string(lerr
), lerr
));
410 if (strcasecmp(attr_res
, ldap_map
.NFSv4_uid_attr
) == 0) {
411 tmp_u
= strtoul(*idstr
, (char **)NULL
, 10);
413 if (tmp_uid
!= tmp_u
||
414 (errno
== ERANGE
&& tmp_u
== ULONG_MAX
)) {
415 IDMAP_LOG(0, ("ERROR: umich_name_to_ids: "
416 "uidNumber too long converting '%s'\n",
418 ldap_memfree(attr_res
);
419 ldap_value_free(idstr
);
423 } else if (strcasecmp(attr_res
, ldap_map
.NFSv4_gid_attr
) == 0) {
424 tmp_g
= strtoul(*idstr
, (char **)NULL
, 10);
426 if (tmp_gid
!= tmp_g
||
427 (errno
== ERANGE
&& tmp_g
== ULONG_MAX
)) {
428 IDMAP_LOG(0, ("ERROR: umich_name_to_ids: "
429 "gidNumber too long converting '%s'\n",
431 ldap_memfree(attr_res
);
432 ldap_value_free(idstr
);
437 IDMAP_LOG(0, ("umich_name_to_ids: received attr "
438 "'%s' ???\n", attr_res
));
439 ldap_memfree(attr_res
);
440 ldap_value_free(idstr
);
443 ldap_memfree(attr_res
);
444 ldap_value_free(idstr
);
452 ldap_msgfree(result
);
459 umich_id_to_name(uid_t id
, int idtype
, char **name
, size_t len
,
460 struct umich_ldap_info
*linfo
)
463 struct timeval timeout
= {
464 .tv_sec
= linfo
->ldap_timeout
,
466 LDAPMessage
*result
= NULL
, *entry
;
468 char **names
= NULL
, filter
[LDAP_FILT_MAXSIZ
], *base
;
472 int count
= 0, err
, lerr
, f_len
;
476 if (name
== NULL
|| linfo
== NULL
|| linfo
->server
== NULL
||
477 linfo
->people_tree
== NULL
|| linfo
->group_tree
== NULL
)
480 snprintf(idstr
, sizeof(idstr
), "%d", id
);
483 if (idtype
== IDTYPE_USER
) {
484 if ((f_len
= snprintf(filter
, LDAP_FILT_MAXSIZ
,
485 "(&(objectClass=%s)(%s=%s))",
486 ldap_map
.NFSv4_person_objcls
,
487 ldap_map
.NFSv4_uid_attr
, idstr
))
488 == LDAP_FILT_MAXSIZ
) {
489 IDMAP_LOG(0, ("ERROR: umich_id_to_name: "
490 "uid filter too long!\n"));
493 base
= linfo
->people_tree
;
494 } else if (idtype
== IDTYPE_GROUP
) {
495 if ((f_len
= snprintf(filter
, LDAP_FILT_MAXSIZ
,
496 "(&(objectClass=%s)(%s=%s))",
497 ldap_map
.NFSv4_group_objcls
,
498 ldap_map
.NFSv4_gid_attr
,idstr
))
499 == LDAP_FILT_MAXSIZ
) {
500 IDMAP_LOG(0, ("ERROR: umich_id_to_name: "
501 "gid filter too long!\n"));
504 base
= linfo
->group_tree
;
506 IDMAP_LOG(0, ("ERROR: umich_id_to_name: invalid idtype (%d)\n",
512 if (ldap_init_and_bind(&ld
, &sizelimit
, linfo
))
515 if (idtype
== IDTYPE_USER
)
516 attrs
[0] = ldap_map
.NFSv4_nfsname_attr
;
518 attrs
[0] = ldap_map
.NFSv4_group_nfsname_attr
;
521 err
= ldap_search_st(ld
, base
, LDAP_SCOPE_SUBTREE
,
522 filter
, (char **)attrs
,
523 0, &timeout
, &result
);
527 IDMAP_LOG(2, ("umich_id_to_name: ldap_search_st for "
528 "base '%s, filter '%s': %s (%d)\n", base
, filter
,
529 ldap_err2string(err
), err
));
530 if ((ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &errmsg
) == LDAP_SUCCESS
)
531 && (errmsg
!= NULL
) && (*errmsg
!= '\0')) {
532 IDMAP_LOG(2, ("umich_id_to_name: "
533 "Additional info: %s\n", errmsg
));
534 ldap_memfree(errmsg
);
542 count
= ldap_count_entries(ld
, result
);
546 if (!(entry
= ldap_first_entry(ld
, result
))) {
547 lerr
= ldap_result2error(ld
, result
, 0);
548 IDMAP_LOG(2, ("umich_id_to_name: ldap_first_entry: "
549 "%s (%d)\n", ldap_err2string(lerr
), lerr
));
553 if (!(attr_res
= ldap_first_attribute(ld
, result
, &ber
))) {
554 lerr
= ldap_result2error(ld
, result
, 0);
555 IDMAP_LOG(2, ("umich_id_to_name: ldap_first_attribute: "
556 "%s (%d)\n", ldap_err2string(lerr
), lerr
));
560 if ((names
= ldap_get_values(ld
, result
, attr_res
)) == NULL
) {
561 lerr
= ldap_result2error(ld
, result
, 0);
562 IDMAP_LOG(2, ("umich_id_to_name: ldap_get_values: "
563 "%s (%d)\n", ldap_err2string(lerr
), lerr
));
568 * Verify there is enough room in the output buffer before
569 * copying returned string. (strlen doesn't count the null,
570 * we make sure there is room for the null also, therefore
571 * we use ">=" not just ">")
573 if (strlen(names
[0]) >= len
) {
574 /* not enough space to return the name */
575 IDMAP_LOG(1, ("umich_id_to_name: output buffer size (%d) "
576 "too small to return string, '%s', of length %d\n",
577 len
, names
[0], strlen(names
[0])));
580 strcpy(*name
, names
[0]);
585 ldap_value_free(names
);
586 ldap_memfree(attr_res
);
590 ldap_msgfree(result
);
597 umich_gss_princ_to_grouplist(char *principal
, gid_t
*groups
, int *ngroups
,
598 struct umich_ldap_info
*linfo
)
601 struct timeval timeout
= {
602 .tv_sec
= linfo
->ldap_timeout
,
604 LDAPMessage
*result
, *entry
;
605 char **names
, filter
[LDAP_FILT_MAXSIZ
];
607 int count
= 0, err
= -ENOMEM
, lerr
, f_len
;
609 gid_t
*curr_group
= groups
;
612 if (linfo
== NULL
|| linfo
->server
== NULL
||
613 linfo
->people_tree
== NULL
|| linfo
->group_tree
== NULL
)
617 if (ldap_init_and_bind(&ld
, NULL
, linfo
))
621 * First we need to map the gss principal name to a uid (name) string
624 if ((f_len
= snprintf(filter
, LDAP_FILT_MAXSIZ
,
625 "(&(objectClass=%s)(%s=%s))",
626 ldap_map
.NFSv4_person_objcls
,
627 ldap_map
.GSS_principal_attr
, principal
))
628 == LDAP_FILT_MAXSIZ
) {
629 IDMAP_LOG(0, ("ERROR: umich_gss_princ_to_grouplist: "
630 "filter too long!\n"));
634 attrs
[0] = ldap_map
.NFSv4_acctname_attr
;
637 err
= ldap_search_st(ld
, linfo
->people_tree
, LDAP_SCOPE_SUBTREE
,
638 filter
, attrs
, 0, &timeout
, &result
);
642 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_search_st "
643 "for tree '%s, filter '%s': %s (%d)\n",
644 linfo
->people_tree
, filter
,
645 ldap_err2string(err
), err
));
646 if ((ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &errmsg
) == LDAP_SUCCESS
)
647 && (errmsg
!= NULL
) && (*errmsg
!= '\0')) {
648 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: "
649 "Additional info: %s\n", errmsg
));
650 ldap_memfree(errmsg
);
657 count
= ldap_count_entries(ld
, result
);
659 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: "
660 "ldap account lookup of gssauthname %s returned %d accounts\n",
665 if (!(entry
= ldap_first_entry(ld
, result
))) {
666 lerr
= ldap_result2error(ld
, result
, 0);
667 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_first_entry: "
668 "%s (%d)\n", ldap_err2string(lerr
), lerr
));
672 if ((names
= ldap_get_values(ld
, result
, attrs
[0])) == NULL
) {
673 lerr
= ldap_result2error(ld
, result
, 0);
674 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_get_values: "
675 "%s (%d)\n", ldap_err2string(lerr
), lerr
));
679 if (ldap_info
.memberof_for_groups
) {
682 * Collect the groups the user belongs to
684 if ((f_len
= snprintf(filter
, LDAP_FILT_MAXSIZ
,
685 "(&(objectClass=%s)(%s=%s))",
686 ldap_map
.NFSv4_person_objcls
,
687 ldap_map
.NFSv4_acctname_attr
,
688 names
[0])) == LDAP_FILT_MAXSIZ
) {
689 IDMAP_LOG(2, ("ERROR: umich_gss_princ_to_grouplist: "
690 "filter too long!\n"));
691 ldap_value_free(names
);
695 ldap_value_free(names
);
697 attrs
[0] = ldap_map
.NFSv4_member_of_attr
;
700 err
= ldap_search_st(ld
, linfo
->people_tree
, LDAP_SCOPE_SUBTREE
,
701 filter
, attrs
, 0, &timeout
, &result
);
706 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_search_st "
707 "for tree '%s, filter '%s': %s (%d)\n",
708 linfo
->people_tree
, filter
,
709 ldap_err2string(err
), err
));
710 if ((ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &errmsg
) == LDAP_SUCCESS
)
711 && (errmsg
!= NULL
) && (*errmsg
!= '\0')) {
712 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: "
713 "Additional info: %s\n", errmsg
));
714 ldap_memfree(errmsg
);
721 /* pull the list of groups and place into names */
722 count
= ldap_count_entries(ld
, result
);
724 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: "
725 "ldap group member lookup of gssauthname %s returned %d multiple entries\n",
730 if (!(entry
= ldap_first_entry(ld
, result
))) {
731 lerr
= ldap_result2error(ld
, result
, 0);
732 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_first_entry: "
733 "%s (%d)\n", ldap_err2string(lerr
), lerr
));
737 if ((names
= ldap_get_values(ld
, result
, attrs
[0])) == NULL
) {
738 lerr
= ldap_result2error(ld
, result
, 0);
739 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_get_values: "
740 "%s (%d)\n", ldap_err2string(lerr
), lerr
));
744 /* Count the groups first before doing a lookup of the group.
745 If it exceeds the desired number of groups set the needed value
747 for (i
= 0; names
[i
] != NULL
; i
++);
748 if ( i
> *ngroups
) {
749 ldap_value_free(names
);
751 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: User %s, "
752 "number of groups %d, exceeds requested number %d\n",
753 principal
, i
, *ngroups
));
758 /* Loop through the groupnames (names) and get the group gid */
760 for (i
= 0; names
[i
] != NULL
; i
++){
767 cnptr
= strchr(names
[i
],',');
768 if (cnptr
) *cnptr
= '\0';
771 if (ldap_map
.NFSv4_grouplist_filter
)
772 f_len
= snprintf(filter
, LDAP_FILT_MAXSIZ
,
773 "(&(objectClass=%s)(%s)%s)",
774 ldap_map
.NFSv4_group_objcls
,
776 ldap_map
.NFSv4_grouplist_filter
);
778 f_len
= snprintf(filter
, LDAP_FILT_MAXSIZ
,
779 "(&(objectClass=%s)(%s))",
780 ldap_map
.NFSv4_group_objcls
,
783 if ( f_len
== LDAP_FILT_MAXSIZ
) {
784 IDMAP_LOG(2, ("ERROR: umich_gss_princ_to_grouplist: "
785 "filter too long!\n"));
786 ldap_value_free(names
);
789 attrs
[0] = ldap_map
.NFSv4_gid_attr
;
792 err
= ldap_search_st(ld
, linfo
->group_tree
, LDAP_SCOPE_SUBTREE
,
793 filter
, attrs
, 0, &timeout
, &result
);
797 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_search_st "
798 "for tree '%s, filter '%s': %s (%d)\n",
799 linfo
->group_tree
, filter
,
800 ldap_err2string(err
), err
));
801 if ((ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &errmsg
)==LDAP_SUCCESS
)
803 (errmsg
!= NULL
) && (*errmsg
!= '\0')) {
804 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: "
805 "Additional info: %s\n", errmsg
));
806 ldap_memfree(errmsg
);
811 count
= ldap_count_entries(ld
, result
);
815 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist:"
816 "Group %s has %d gids defined - aborting", names
[i
], count
));
817 ldap_value_free(names
);
822 vals
= ldap_get_values(ld
, result
, ldap_map
.NFSv4_gid_attr
);
824 /* There should be only one gidNumber attribute per group */
825 if ((valcount
= ldap_count_values(vals
)) != 1) {
826 IDMAP_LOG(2, ("DB problem getting gidNumber of "
827 "posixGroup! (count was %d)\n", valcount
));
828 ldap_value_free(vals
);
832 tmp_g
= strtoul(vals
[0], (char **)NULL
, 10);
834 if (tmp_gid
!= tmp_g
||
835 (errno
== ERANGE
&& tmp_g
== ULONG_MAX
)) {
836 IDMAP_LOG(2, ("ERROR: umich_gss_princ_to_grouplist: "
837 "gidNumber too long converting '%s'\n",
839 ldap_value_free(vals
);
842 *curr_group
++ = tmp_gid
;
844 ldap_value_free(vals
);
846 ldap_value_free(names
);
852 * Then determine the groups that uid (name) string is a member of
855 if (ldap_map
.NFSv4_grouplist_filter
)
856 f_len
= snprintf(filter
, LDAP_FILT_MAXSIZ
,
857 "(&(objectClass=%s)(%s=%s)%s)",
858 ldap_map
.NFSv4_group_objcls
,
859 ldap_map
.NFSv4_member_attr
,
861 ldap_map
.NFSv4_grouplist_filter
);
864 f_len
= snprintf(filter
, LDAP_FILT_MAXSIZ
,
865 "(&(objectClass=%s)(%s=%s))",
866 ldap_map
.NFSv4_group_objcls
,
867 ldap_map
.NFSv4_member_attr
,
870 if ( f_len
== LDAP_FILT_MAXSIZ
) {
871 IDMAP_LOG(0, ("ERROR: umich_gss_princ_to_grouplist: "
872 "filter too long!\n"));
873 ldap_value_free(names
);
877 ldap_value_free(names
);
879 attrs
[0] = ldap_map
.NFSv4_gid_attr
;
882 err
= ldap_search_st(ld
, linfo
->group_tree
, LDAP_SCOPE_SUBTREE
,
883 filter
, attrs
, 0, &timeout
, &result
);
888 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: ldap_search_st "
889 "for tree '%s, filter '%s': %s (%d)\n",
890 linfo
->group_tree
, filter
,
891 ldap_err2string(err
), err
));
892 if ((ldap_get_option(ld
, LDAP_OPT_ERROR_STRING
, &errmsg
) == LDAP_SUCCESS
) &&
893 (errmsg
!= NULL
) && (*errmsg
!= '\0')) {
894 IDMAP_LOG(2, ("umich_gss_princ_to_grouplist: "
895 "Additional info: %s\n", errmsg
));
896 ldap_memfree(errmsg
);
903 * If we can't determine count, return that error
904 * If we have nothing to return, return success
905 * If we have more than they asked for, tell them the
906 * number required and return an error
908 count
= ldap_count_entries(ld
, result
);
919 if (count
> *ngroups
) {
929 for (entry
= ldap_first_entry(ld
, result
);
931 entry
= ldap_next_entry(ld
, entry
)) {
938 vals
= ldap_get_values(ld
, entry
, ldap_map
.NFSv4_gid_attr
);
940 /* There should be only one gidNumber attribute per group */
941 if ((valcount
= ldap_count_values(vals
)) != 1) {
942 IDMAP_LOG(0, ("DB problem getting gidNumber of "
943 "posixGroup! (count was %d)\n", valcount
));
946 tmp_g
= strtoul(vals
[0], (char **)NULL
, 10);
948 if (tmp_gid
!= tmp_g
||
949 (errno
== ERANGE
&& tmp_g
== ULONG_MAX
)) {
950 IDMAP_LOG(0, ("ERROR: umich_gss_princ_to_grouplist: "
951 "gidNumber too long converting '%s'\n",
953 ldap_value_free(vals
);
956 *curr_group
++ = tmp_gid
;
957 ldap_value_free(vals
);
970 * principal: krb5 - princ@realm, use KrbName ldap attribute
971 * spkm3 - X.509 dn, use X509Name ldap attribute
974 umichldap_gss_princ_to_ids(char *secname
, char *principal
,
975 uid_t
*uid
, gid_t
*gid
, extra_mapping_params
**ex
)
981 if ((strcmp(secname
, "krb5") != 0) && (strcmp(secname
, "spkm3") != 0)) {
982 IDMAP_LOG(0, ("ERROR: umichldap_gss_princ_to_ids: "
983 "invalid secname '%s'\n", secname
));
987 err
= umich_name_to_ids(principal
, IDTYPE_USER
, &rtnd_uid
, &rtnd_gid
,
988 ldap_map
.GSS_principal_attr
, &ldap_info
);
999 umichldap_name_to_uid(char *name
, uid_t
*uid
)
1003 return umich_name_to_ids(name
, IDTYPE_USER
, uid
,
1004 &gid
, ldap_map
.NFSv4_nfsname_attr
, &ldap_info
);
1008 umichldap_name_to_gid(char *name
, gid_t
*gid
)
1012 return umich_name_to_ids(name
, IDTYPE_GROUP
, &uid
, gid
,
1013 ldap_map
.NFSv4_group_nfsname_attr
, &ldap_info
);
1017 umichldap_uid_to_name(uid_t uid
, char *domain
, char *name
, size_t len
)
1019 return umich_id_to_name(uid
, IDTYPE_USER
, &name
, len
, &ldap_info
);
1023 umichldap_gid_to_name(gid_t gid
, char *domain
, char *name
, size_t len
)
1025 return umich_id_to_name(gid
, IDTYPE_GROUP
, &name
, len
, &ldap_info
);
1029 umichldap_gss_princ_to_grouplist(char *secname
, char *principal
,
1030 gid_t
*groups
, int *ngroups
, extra_mapping_params
**ex
)
1034 if ((strcmp(secname
, "krb5") != 0) && (strcmp(secname
, "spkm3") != 0)) {
1035 IDMAP_LOG(0, ("ERROR: umichldap_gss_princ_to_grouplist: "
1036 "invalid secname '%s'\n", secname
));
1040 return umich_gss_princ_to_grouplist(principal
, groups
, ngroups
,
1045 * TLS connections require that the hostname we specify matches
1046 * the hostname in the certificate that the server uses.
1047 * Get a canonical name for the host specified in the config file.
1050 get_canonical_hostname(const char *inname
)
1053 struct addrinfo
*ap
, aihints
;
1054 char *return_name
= NULL
;
1055 char tmphost
[NI_MAXHOST
];
1057 memset(&aihints
, 0, sizeof(aihints
));
1058 aihints
.ai_socktype
= SOCK_STREAM
;
1059 aihints
.ai_flags
= AI_CANONNAME
;
1060 aihints
.ai_family
= PF_INET
;
1061 aierr
= getaddrinfo(inname
, NULL
, &aihints
, &ap
);
1064 /* We want to customize some messages. */
1067 msg
= "host unknown";
1070 msg
= gai_strerror(aierr
);
1073 IDMAP_LOG(1, ("%s: '%s': %s\n", __FUNCTION__
, inname
, msg
));
1077 IDMAP_LOG(1, ("%s: no addresses for host '%s'?\n",
1078 __FUNCTION__
, inname
));
1082 error
= getnameinfo (ap
->ai_addr
, ap
->ai_addrlen
, tmphost
,
1083 sizeof(tmphost
), NULL
, 0, 0);
1085 IDMAP_LOG(1, ("%s: getnameinfo for host '%s' failed (%d)\n",
1086 __FUNCTION__
, inname
));
1089 return_name
= strdup (tmphost
);
1098 umichldap_init(void)
1100 char *tssl
, *canonicalize
, *memberof
;
1101 int missing_server
= 0, missing_base
= 0;
1102 char missing_msg
[128] = "";
1103 char *server_in
, *canon_name
;
1105 server_in
= conf_get_str(LDAP_SECTION
, "LDAP_server");
1106 ldap_info
.base
= conf_get_str(LDAP_SECTION
, "LDAP_base");
1107 ldap_info
.people_tree
= conf_get_str(LDAP_SECTION
, "LDAP_people_base");
1108 ldap_info
.group_tree
= conf_get_str(LDAP_SECTION
, "LDAP_group_base");
1109 ldap_info
.user_dn
= conf_get_str(LDAP_SECTION
, "LDAP_user_dn");
1110 ldap_info
.passwd
= conf_get_str(LDAP_SECTION
, "LDAP_passwd");
1111 tssl
= conf_get_str_with_def(LDAP_SECTION
, "LDAP_use_ssl", "false");
1112 if ((strcasecmp(tssl
, "true") == 0) ||
1113 (strcasecmp(tssl
, "on") == 0) ||
1114 (strcasecmp(tssl
, "yes") == 0))
1115 ldap_info
.use_ssl
= 1;
1117 ldap_info
.use_ssl
= 0;
1118 ldap_info
.ca_cert
= conf_get_str(LDAP_SECTION
, "LDAP_CA_CERT");
1119 /* vary the default port depending on whether they use SSL or not */
1120 ldap_info
.port
= conf_get_num(LDAP_SECTION
, "LDAP_port",
1121 (ldap_info
.use_ssl
) ?
1122 LDAPS_PORT
: LDAP_PORT
);
1124 /* Verify required information is supplied */
1125 if (server_in
== NULL
|| strlen(server_in
) == 0)
1126 strncat(missing_msg
, "LDAP_server ", sizeof(missing_msg
));
1127 if (ldap_info
.base
== NULL
|| strlen(ldap_info
.base
) == 0)
1128 strncat(missing_msg
, "LDAP_base ", sizeof(missing_msg
));
1129 if (strlen(missing_msg
) != 0) {
1130 IDMAP_LOG(0, ("umichldap_init: Missing required information: "
1131 "%s\n", missing_msg
));
1135 ldap_info
.server
= server_in
;
1136 canonicalize
= conf_get_str_with_def(LDAP_SECTION
, "LDAP_canonicalize_name", "yes");
1137 if ((strcasecmp(canonicalize
, "true") == 0) ||
1138 (strcasecmp(canonicalize
, "on") == 0) ||
1139 (strcasecmp(canonicalize
, "yes") == 0)) {
1140 canon_name
= get_canonical_hostname(server_in
);
1141 if (canon_name
== NULL
)
1142 IDMAP_LOG(0, ("umichldap_init: Warning! Unable to "
1143 "canonicalize server name '%s' as requested.\n",
1146 ldap_info
.server
= canon_name
;
1149 /* get the ldap mapping attributes/objectclasses (all have defaults) */
1150 ldap_map
.NFSv4_person_objcls
=
1151 conf_get_str_with_def(LDAP_SECTION
, "NFSv4_person_objectclass",
1152 DEFAULT_UMICH_OBJCLASS_REMOTE_PERSON
);
1154 ldap_map
.NFSv4_group_objcls
=
1155 conf_get_str_with_def(LDAP_SECTION
, "NFSv4_group_objectclass",
1156 DEFAULT_UMICH_OBJCLASS_REMOTE_GROUP
);
1158 ldap_map
.NFSv4_nfsname_attr
=
1159 conf_get_str_with_def(LDAP_SECTION
, "NFSv4_name_attr",
1160 DEFAULT_UMICH_ATTR_NFSNAME
);
1162 ldap_map
.NFSv4_uid_attr
=
1163 conf_get_str_with_def(LDAP_SECTION
, "NFSv4_uid_attr",
1164 DEFAULT_UMICH_ATTR_UIDNUMBER
);
1166 ldap_map
.NFSv4_acctname_attr
=
1167 conf_get_str_with_def(LDAP_SECTION
, "NFSv4_acctname_attr",
1168 DEFAULT_UMICH_ATTR_ACCTNAME
);
1170 ldap_map
.NFSv4_group_nfsname_attr
=
1171 conf_get_str_with_def(LDAP_SECTION
, "NFSv4_group_attr",
1172 DEFAULT_UMICH_ATTR_GROUP_NFSNAME
);
1174 ldap_map
.NFSv4_gid_attr
=
1175 conf_get_str_with_def(LDAP_SECTION
, "NFSv4_gid_attr",
1176 DEFAULT_UMICH_ATTR_GIDNUMBER
);
1178 ldap_map
.NFSv4_member_attr
=
1179 conf_get_str_with_def(LDAP_SECTION
, "NFSv4_member_attr",
1180 DEFAULT_UMICH_ATTR_MEMBERUID
);
1182 ldap_map
.GSS_principal_attr
=
1183 conf_get_str_with_def(LDAP_SECTION
, "GSS_principal_attr",
1184 DEFAULT_UMICH_ATTR_GSSAUTHNAME
);
1186 ldap_map
.NFSv4_grouplist_filter
=
1187 conf_get_str_with_def(LDAP_SECTION
, "NFSv4_grouplist_filter",
1190 ldap_map
.NFSv4_member_of_attr
=
1191 conf_get_str_with_def(LDAP_SECTION
, "NFSv4_member_of_attr",
1192 DEFAULT_UMICH_ATTR_MEMBEROF
);
1194 ldap_info
.ldap_timeout
=
1195 conf_get_num(LDAP_SECTION
, "LDAP_timeout_seconds",
1196 DEFAULT_UMICH_SEARCH_TIMEOUT
);
1200 * Some LDAP servers do a better job with indexing where searching
1201 * through all the groups searching for the user in the memberuid
1202 * list. Others like SunOne directory that search can takes minutes
1203 * if there are thousands of groups. So setting
1204 * LDAP_use_memberof_for_groups to true in the configuration file
1205 * will use the memberof lists of the account and search through
1206 * only those groups to obtain gids.
1208 memberof
= conf_get_str_with_def(LDAP_SECTION
,
1209 "LDAP_use_memberof_for_groups", "false");
1210 if ((strcasecmp(memberof
, "true") == 0) ||
1211 (strcasecmp(memberof
, "on") == 0) ||
1212 (strcasecmp(memberof
, "yes") == 0))
1213 ldap_info
.memberof_for_groups
= 1;
1215 ldap_info
.memberof_for_groups
= 0;
1218 * If they specified a search base for the
1219 * people tree or group tree we use that.
1220 * Otherwise we use the default search base.
1221 * Note: We no longer append the default base to the tree --
1222 * that should already be specified.
1223 * this functions much like the NSS_LDAP modules
1225 if (ldap_info
.people_tree
== NULL
|| strlen(ldap_info
.people_tree
) == 0)
1226 ldap_info
.people_tree
= ldap_info
.base
;
1227 if (ldap_info
.group_tree
== NULL
|| strlen(ldap_info
.group_tree
) == 0)
1228 ldap_info
.group_tree
= ldap_info
.base
;
1230 if (ldap_info
.use_ssl
&& ldap_info
.ca_cert
== NULL
) {
1231 IDMAP_LOG(0, ("umichldap_init: You must specify LDAP_ca_cert "
1232 "with LDAP_use_ssl=yes\n"));
1237 /* print out some good debugging info */
1238 IDMAP_LOG(1, ("umichldap_init: canonicalize_name: %s\n",
1240 IDMAP_LOG(1, ("umichldap_init: server : %s (from config value '%s')\n",
1241 ldap_info
.server
, server_in
));
1242 IDMAP_LOG(1, ("umichldap_init: port : %d\n", ldap_info
.port
));
1243 IDMAP_LOG(1, ("umichldap_init: people : %s\n", ldap_info
.people_tree
));
1244 IDMAP_LOG(1, ("umichldap_init: groups : %s\n", ldap_info
.group_tree
));
1246 IDMAP_LOG(1, ("umichldap_init: user_dn : %s\n",
1247 (ldap_info
.user_dn
&& strlen(ldap_info
.user_dn
) != 0)
1248 ? ldap_info
.user_dn
: "<not-supplied>"));
1249 /* Don't print actual password into the log. */
1250 IDMAP_LOG(1, ("umichldap_init: passwd : %s\n",
1251 (ldap_info
.passwd
&& strlen(ldap_info
.passwd
) != 0) ?
1252 "<supplied>" : "<not-supplied>"));
1253 IDMAP_LOG(1, ("umichldap_init: use_ssl : %s\n",
1254 ldap_info
.use_ssl
? "yes" : "no"));
1255 IDMAP_LOG(1, ("umichldap_init: ca_cert : %s\n",
1256 ldap_info
.ca_cert
? ldap_info
.ca_cert
: "<not-supplied>"));
1257 IDMAP_LOG(1, ("umichldap_init: use_memberof_for_groups : %s\n",
1258 ldap_info
.memberof_for_groups
? "yes" : "no"));
1260 IDMAP_LOG(1, ("umichldap_init: NFSv4_person_objectclass : %s\n",
1261 ldap_map
.NFSv4_person_objcls
));
1262 IDMAP_LOG(1, ("umichldap_init: NFSv4_nfsname_attr : %s\n",
1263 ldap_map
.NFSv4_nfsname_attr
));
1264 IDMAP_LOG(1, ("umichldap_init: NFSv4_acctname_attr : %s\n",
1265 ldap_map
.NFSv4_acctname_attr
));
1266 IDMAP_LOG(1, ("umichldap_init: NFSv4_uid_attr : %s\n",
1267 ldap_map
.NFSv4_uid_attr
));
1268 IDMAP_LOG(1, ("umichldap_init: NFSv4_group_objectclass : %s\n",
1269 ldap_map
.NFSv4_group_objcls
));
1270 IDMAP_LOG(1, ("umichldap_init: NFSv4_gid_attr : %s\n",
1271 ldap_map
.NFSv4_gid_attr
));
1272 IDMAP_LOG(1, ("umichldap_init: NFSv4_group_nfsname_attr : %s\n",
1273 ldap_map
.NFSv4_group_nfsname_attr
));
1274 IDMAP_LOG(1, ("umichldap_init: NFSv4_member_attr : %s\n",
1275 ldap_map
.NFSv4_member_attr
));
1276 IDMAP_LOG(1, ("umichldap_init: NFSv4_member_of_attr : %s\n",
1277 ldap_map
.NFSv4_member_of_attr
));
1278 IDMAP_LOG(1, ("umichldap_init: NFSv4_grouplist_filter : %s\n",
1279 ldap_map
.NFSv4_grouplist_filter
?
1280 ldap_map
.NFSv4_grouplist_filter
: "<not-specified>"));
1281 IDMAP_LOG(1, ("umichldap_init: GSS_principal_attr : %s\n",
1282 ldap_map
.GSS_principal_attr
));
1289 /* The external interface */
1291 struct trans_func umichldap_trans
= {
1292 .name
= "umich_ldap",
1293 .init
= umichldap_init
,
1294 .princ_to_ids
= umichldap_gss_princ_to_ids
,
1295 .name_to_uid
= umichldap_name_to_uid
,
1296 .name_to_gid
= umichldap_name_to_gid
,
1297 .uid_to_name
= umichldap_uid_to_name
,
1298 .gid_to_name
= umichldap_gid_to_name
,
1299 .gss_princ_to_grouplist
= umichldap_gss_princ_to_grouplist
,
1302 struct trans_func
*libnfsidmap_plugin_init()
1304 return (&umichldap_trans
);