Initial import
[samba] / source / sam / idmap_ad.c
1 /*
2  *  idmap_ad: map between Active Directory and RFC 2307 or "Services for Unix" (SFU) Accounts
3  *
4  * Unix SMB/CIFS implementation.
5  *
6  * Winbind ADS backend functions
7  *
8  * Copyright (C) Andrew Tridgell 2001
9  * Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003
10  * Copyright (C) Gerald (Jerry) Carter 2004
11  * Copyright (C) Luke Howard 2001-2004
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26  */
27
28 #include "includes.h"
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_IDMAP
32
33 #ifndef ATTR_UIDNUMBER
34 #define ATTR_UIDNUMBER ADS_ATTR_SFU_UIDNUMBER_OID
35 #endif
36
37 #ifndef ATTR_GIDNUMBER
38 #define ATTR_GIDNUMBER ADS_ATTR_SFU_GIDNUMBER_OID
39 #endif
40
41 #define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
42
43 NTSTATUS init_module(void);
44
45 static ADS_STRUCT *ad_idmap_ads = NULL;
46 static char *ad_idmap_uri = NULL;
47
48 static char *attr_uidnumber = NULL;
49 static char *attr_gidnumber = NULL;
50
51 static BOOL ad_idmap_check_attr_mapping(ADS_STRUCT *ads)
52 {
53         if (attr_uidnumber != NULL && attr_gidnumber != NULL) {
54                 return True;
55         }
56
57         if (use_nss_info("sfu")) {
58         
59                 if (!ads_check_sfu_mapping(ads)) {
60                         DEBUG(0,("ad_idmap_check_attr_mapping: failed to check for SFU schema\n"));
61                         return False;
62                 }
63
64                 attr_uidnumber = SMB_STRDUP(ads->schema.sfu_uidnumber_attr);
65                 attr_gidnumber = SMB_STRDUP(ads->schema.sfu_gidnumber_attr);
66
67         } else {
68                 attr_uidnumber = SMB_STRDUP("uidNumber");
69                 attr_gidnumber = SMB_STRDUP("gidNumber");
70         }
71
72         return True;
73 }
74
75 static ADS_STRUCT *ad_idmap_cached_connection(void)
76 {
77         ADS_STRUCT *ads;
78         ADS_STATUS status;
79         BOOL local = False;
80
81 #ifdef ADS_AUTH_EXTERNAL_BIND
82         local = ((strncmp(ad_idmap_uri, "ldapi://", sizeof("ldapi://") - 1)) == 0);
83 #endif /* ADS_AUTH_EXTERNAL_BIND */
84
85         if (ad_idmap_ads != NULL) {
86                 ads = ad_idmap_ads;
87
88                 /* check for a valid structure */
89
90                 DEBUG(7, ("Current tickets expire at %d, time is now %d\n",
91                           (uint32) ads->auth.expire, (uint32) time(NULL)));
92                 if ( ads->config.realm && (ads->auth.expire > time(NULL))) {
93                         return ads;
94                 } else {
95                         /* we own this ADS_STRUCT so make sure it goes away */
96                         ads->is_mine = True;
97                         ads_destroy( &ads );
98                         ads_kdestroy(WINBIND_CCACHE_NAME);
99                         ad_idmap_ads = NULL;
100                 }
101         }
102
103         if (!local) {
104                 /* we don't want this to affect the users ccache */
105                 setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
106         }
107
108         ads = ads_init(NULL, NULL, NULL);
109         if (!ads) {
110                 DEBUG(1,("ads_init failed\n"));
111                 return NULL;
112         }
113
114         /* if ad_imap_uri is not empty we try to connect to
115          * the given URI in smb.conf. Else try to connect to
116          * one of the DCs
117          */
118         if (*ad_idmap_uri != '\0') {
119                 ads->server.ldap_uri = SMB_STRDUP(ad_idmap_uri);
120                 if (ads->server.ldap_uri == NULL) {
121                         return NULL;
122                 }
123         }
124         else {
125                 ads->server.ldap_uri    = NULL;
126                 ads->server.ldap_server = NULL;
127         }
128
129 #ifdef ADS_AUTH_EXTERNAL_BIND
130         if (local)
131                 ads->auth.flags |= ADS_AUTH_EXTERNAL_BIND;
132         else
133 #endif
134         {
135                 /* the machine acct password might have change - fetch it every time */
136                 SAFE_FREE(ads->auth.password);
137                 ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
138
139                 SAFE_FREE(ads->auth.realm);
140                 ads->auth.realm = SMB_STRDUP(lp_realm());
141         }
142
143         status = ads_connect(ads);
144         if (!ADS_ERR_OK(status)) {
145                 DEBUG(1, ("ad_idmap_init: failed to connect to AD\n"));
146                 ads_destroy(&ads);
147                 return NULL;
148         }
149
150         ads->is_mine = False;
151
152         if (!ad_idmap_check_attr_mapping(ads)) {
153                 DEBUG(1, ("ad_idmap_init: failed to check attribute mapping\n"));
154                 return NULL;
155         }
156
157         ad_idmap_ads = ads;
158         return ads;
159 }
160
161 static NTSTATUS ad_idmap_init(char *uri)
162 {
163         ad_idmap_uri = SMB_STRDUP(uri);
164         if (ad_idmap_uri == NULL) {
165                 return NT_STATUS_NO_MEMORY;
166         }
167
168         return NT_STATUS_OK;
169 }
170
171 static NTSTATUS ad_idmap_get_sid_from_id(DOM_SID *sid, unid_t unid, int id_type)
172 {
173         ADS_STATUS rc;
174         NTSTATUS status = NT_STATUS_NONE_MAPPED;
175         const char *attrs[] = { "objectSid", NULL };
176         void *res = NULL;
177         void *msg = NULL;
178         char *expr = NULL;
179         fstring sid_string;
180         int count;
181         ADS_STRUCT *ads;
182
183         if (sid == NULL) {
184                 return NT_STATUS_INVALID_PARAMETER;
185         }
186
187         ads = ad_idmap_cached_connection();
188         if (ads == NULL) {
189                 DEBUG(1, ("ad_idmap_get_id_from_sid ADS uninitialized\n"));
190                 return NT_STATUS_NOT_SUPPORTED;
191         }
192
193         switch (id_type & ID_TYPEMASK) {
194                 case ID_USERID:
195                         if (asprintf(&expr, "(&(|(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d))(%s=%d))",
196                                 ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST,
197                                 ATTR_UIDNUMBER, (int)unid.uid) == -1) {
198                                 return NT_STATUS_NO_MEMORY;
199                         }
200                         break;
201                 case ID_GROUPID:
202                         if (asprintf(&expr, "(&(|(sAMAccountType=%d)(sAMAccountType=%d))(%s=%d))",
203                                 ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP,
204                                 ATTR_GIDNUMBER, (int)unid.gid) == -1) {
205                                 return NT_STATUS_NO_MEMORY;
206                         }
207                         break;
208                 default:
209                         return NT_STATUS_INVALID_PARAMETER;
210                         break;
211         }
212
213         rc = ads_search_retry(ads, &res, expr, attrs);
214         free(expr);
215         if (!ADS_ERR_OK(rc)) {
216                 DEBUG(1, ("ad_idmap_get_sid_from_id: ads_search: %s\n", ads_errstr(rc)));
217                 goto done;
218         }
219
220         count = ads_count_replies(ads, res);
221         if (count == 0) {
222                 DEBUG(1, ("ad_idmap_get_sid_from_id: ads_count_replies: no results\n"));
223                 goto done;
224         } else if (count != 1) {
225                 DEBUG(1, ("ad_idmap_get_sid_from_id: ads_count_replies: incorrect cardinality\n"));
226                 goto done;
227         }
228
229         msg = ads_first_entry(ads, res);
230         if (msg == NULL) {
231                 DEBUG(1, ("ad_idmap_get_sid_from_id: ads_first_entry: could not retrieve search result\n"));
232                 goto done;
233         }
234
235         if (!ads_pull_sid(ads, msg, "objectSid", sid)) {
236                 DEBUG(1, ("ad_idmap_get_sid_from_id: ads_pull_sid: could not retrieve SID from entry\n"));
237                 goto done;
238         }
239
240         status = NT_STATUS_OK;
241         DEBUG(1, ("ad_idmap_get_sid_from_id mapped POSIX %s %d to SID [%s]\n",
242                 (id_type == ID_GROUPID) ? "GID" : "UID", (int)unid.uid,
243                 sid_to_string(sid_string, sid)));
244
245 done:
246         if (res != NULL) {
247                 ads_msgfree(ads, res);
248         }
249
250         return status;
251 }
252
253 static NTSTATUS ad_idmap_get_id_from_sid(unid_t *unid, int *id_type, const DOM_SID *sid)
254 {
255         ADS_STATUS rc;
256         NTSTATUS status = NT_STATUS_NONE_MAPPED;
257         const char *attrs[] = { "sAMAccountType", ATTR_UIDNUMBER, ATTR_GIDNUMBER, NULL };
258         void *res = NULL;
259         void *msg = NULL;
260         char *expr = NULL;
261         uint32 atype, uid;
262         char *sidstr;
263         fstring sid_string;
264         int count;
265         ADS_STRUCT *ads;
266
267         if (unid == NULL) {
268                 return NT_STATUS_INVALID_PARAMETER;
269         }
270
271         ads = ad_idmap_cached_connection();
272         if (ads == NULL) {
273                 DEBUG(1, ("ad_idmap_get_id_from_sid ADS uninitialized\n"));
274                 return NT_STATUS_NOT_SUPPORTED;
275         }
276
277         sidstr = sid_binstring(sid);
278         if (asprintf(&expr, "(objectSid=%s)", sidstr) == -1) {
279                 free(sidstr);
280                 return NT_STATUS_NO_MEMORY;
281         }
282
283         rc = ads_search_retry(ads, &res, expr, attrs);
284         free(sidstr);
285         free(expr);
286         if (!ADS_ERR_OK(rc)) {
287                 DEBUG(1, ("ad_idmap_get_id_from_sid: ads_search: %s\n", ads_errstr(rc)));
288                 goto done;
289         }
290
291         count = ads_count_replies(ads, res);
292         if (count == 0) {
293                 DEBUG(1, ("ad_idmap_get_id_from_sid: ads_count_replies: no results\n"));
294                 goto done;
295         } else if (count != 1) {
296                 DEBUG(1, ("ad_idmap_get_id_from_sid: ads_count_replies: incorrect cardinality\n"));
297                 goto done;
298         }
299
300         msg = ads_first_entry(ads, res);
301         if (msg == NULL) {
302                 DEBUG(1, ("ad_idmap_get_id_from_sid: ads_first_entry: could not retrieve search result\n"));
303                 goto done;
304         }
305
306         if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
307                 DEBUG(1, ("ad_idmap_get_id_from_sid: ads_pull_uint32: could not read SAM account type\n"));
308                 goto done;
309         }
310
311         switch (atype & 0xF0000000) {
312         case ATYPE_SECURITY_GLOBAL_GROUP:
313         case ATYPE_SECURITY_LOCAL_GROUP:
314                 *id_type = ID_GROUPID;
315                 break;
316         case ATYPE_NORMAL_ACCOUNT:
317         case ATYPE_WORKSTATION_TRUST:
318         case ATYPE_INTERDOMAIN_TRUST:
319                 *id_type = ID_USERID;
320                 break;
321         default:
322                 DEBUG(1, ("ad_idmap_get_id_from_sid: unrecognized SAM account type %08x\n", atype));
323                 goto done;
324                 break;
325         }
326
327         if (!ads_pull_uint32(ads, msg, (*id_type == ID_GROUPID) ? attr_gidnumber : attr_uidnumber, &uid)) {
328                 DEBUG(1, ("ad_idmap_get_id_from_sid: ads_pull_uint32: could not read attribute '%s'\n",
329                         (*id_type == ID_GROUPID) ? attr_gidnumber : attr_uidnumber));
330                 goto done;
331         }
332
333         unid->uid = (uid_t)uid;
334
335         status = NT_STATUS_OK;
336         DEBUG(1, ("ad_idmap_get_id_from_sid mapped SID [%s] to POSIX %s %d\n",
337                 sid_to_string(sid_string, sid),
338                 (*id_type == ID_GROUPID) ? "GID" : "UID", uid));
339
340 done:
341         if (res != NULL) {
342                 ads_msgfree(ads, res);
343         }
344
345         return status;
346
347 }
348
349 static NTSTATUS ad_idmap_set_mapping(const DOM_SID *sid, unid_t id, int id_type)
350 {
351         /* Not supported, and probably won't be... */
352         /* (It's not particularly feasible with a single-master model.) */
353
354         return NT_STATUS_NOT_IMPLEMENTED;
355 }
356
357 static NTSTATUS ad_idmap_close(void)
358 {
359         ADS_STRUCT *ads = ad_idmap_ads;
360
361         if (ads != NULL) {
362                 /* we own this ADS_STRUCT so make sure it goes away */
363                 ads->is_mine = True;
364                 ads_destroy( &ads );
365                 ad_idmap_ads = NULL;
366         }
367
368         SAFE_FREE(attr_uidnumber);
369         SAFE_FREE(attr_gidnumber);
370         
371         return NT_STATUS_OK;
372 }
373
374 /* New for beta3 */
375 static NTSTATUS ad_idmap_allocate_rid(uint32 *rid, int rid_type)
376 {
377         return NT_STATUS_NOT_IMPLEMENTED;
378 }
379
380 static NTSTATUS ad_idmap_allocate_id(unid_t *id, int id_type)
381 {
382         return NT_STATUS_NOT_IMPLEMENTED;
383 }
384
385 static void ad_idmap_status(void)
386 {
387         DEBUG(0, ("AD IDMAP Status not available\n"));
388 }
389
390 static struct idmap_methods ad_methods = {
391         ad_idmap_init,
392         ad_idmap_allocate_rid,
393         ad_idmap_allocate_id,
394         ad_idmap_get_sid_from_id,
395         ad_idmap_get_id_from_sid,
396         ad_idmap_set_mapping,
397         ad_idmap_close,
398         ad_idmap_status
399 };
400
401
402 /* support for new authentication subsystem */
403 NTSTATUS init_module(void)
404 {
405         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ad", &ad_methods);
406 }
407