r20110: Fix interaction between paranoid malloc checker
[tprouty/samba.git] / 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 #define WINBIND_CCACHE_NAME "MEMORY:winbind_ccache"
34
35 NTSTATUS init_module(void);
36
37 static ADS_STRUCT *ad_idmap_ads = NULL;
38
39 static char *attr_uidnumber = NULL;
40 static char *attr_gidnumber = NULL;
41
42 static ADS_STATUS ad_idmap_check_attr_mapping(ADS_STRUCT *ads)
43 {
44         ADS_STATUS status;
45         enum wb_posix_mapping map_type;
46
47         if (attr_uidnumber != NULL && attr_gidnumber != NULL) {
48                 return ADS_ERROR(LDAP_SUCCESS);
49         }
50
51         SMB_ASSERT(ads->server.workgroup);
52
53         map_type = get_nss_info(ads->server.workgroup);
54
55         if ((map_type == WB_POSIX_MAP_SFU) ||
56             (map_type == WB_POSIX_MAP_RFC2307)) {
57
58                 status = ads_check_posix_schema_mapping(ads, map_type);
59                 if (ADS_ERR_OK(status)) {
60                         attr_uidnumber = SMB_STRDUP(ads->schema.posix_uidnumber_attr);
61                         attr_gidnumber = SMB_STRDUP(ads->schema.posix_gidnumber_attr);
62                         ADS_ERROR_HAVE_NO_MEMORY(attr_uidnumber);
63                         ADS_ERROR_HAVE_NO_MEMORY(attr_gidnumber);
64                         return ADS_ERROR(LDAP_SUCCESS);
65                 } else {
66                         DEBUG(0,("ads_check_posix_schema_mapping failed: %s\n", ads_errstr(status)));
67                         /* return status; */
68                 }
69         }
70         
71         /* fallback to XAD defaults */
72         attr_uidnumber = SMB_STRDUP("uidNumber");
73         attr_gidnumber = SMB_STRDUP("gidNumber");
74         ADS_ERROR_HAVE_NO_MEMORY(attr_uidnumber);
75         ADS_ERROR_HAVE_NO_MEMORY(attr_gidnumber);
76
77         return ADS_ERROR(LDAP_SUCCESS);
78 }
79
80 static ADS_STRUCT *ad_idmap_cached_connection(void)
81 {
82         ADS_STRUCT *ads;
83         ADS_STATUS status;
84         BOOL local = False;
85
86         if (ad_idmap_ads != NULL) {
87                 ads = ad_idmap_ads;
88
89                 /* check for a valid structure */
90
91                 DEBUG(7, ("Current tickets expire at %d, time is now %d\n",
92                           (uint32) ads->auth.expire, (uint32) time(NULL)));
93                 if ( ads->config.realm && (ads->auth.expire > time(NULL))) {
94                         return ads;
95                 } else {
96                         /* we own this ADS_STRUCT so make sure it goes away */
97                         ads->is_mine = True;
98                         ads_destroy( &ads );
99                         ads_kdestroy(WINBIND_CCACHE_NAME);
100                         ad_idmap_ads = NULL;
101                 }
102         }
103
104         if (!local) {
105                 /* we don't want this to affect the users ccache */
106                 setenv("KRB5CCNAME", WINBIND_CCACHE_NAME, 1);
107         }
108
109         ads = ads_init(lp_realm(), lp_workgroup(), NULL);
110         if (!ads) {
111                 DEBUG(1,("ads_init failed\n"));
112                 return NULL;
113         }
114
115         /* the machine acct password might have change - fetch it every time */
116         SAFE_FREE(ads->auth.password);
117         ads->auth.password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL);
118
119         SAFE_FREE(ads->auth.realm);
120         ads->auth.realm = SMB_STRDUP(lp_realm());
121
122         status = ads_connect(ads);
123         if (!ADS_ERR_OK(status)) {
124                 DEBUG(1, ("ad_idmap_init: failed to connect to AD\n"));
125                 ads_destroy(&ads);
126                 return NULL;
127         }
128
129         ads->is_mine = False;
130
131         status = ad_idmap_check_attr_mapping(ads);
132         if (!ADS_ERR_OK(status)) {
133                 DEBUG(1, ("ad_idmap_init: failed to check attribute mapping\n"));
134                 return NULL;
135         }
136
137         ad_idmap_ads = ads;
138         return ads;
139 }
140
141 /* no op */
142 static NTSTATUS ad_idmap_init(const char *uri)
143 {
144         return NT_STATUS_OK;
145 }
146
147 static NTSTATUS ad_idmap_get_sid_from_id(DOM_SID *sid, unid_t unid, enum idmap_type id_type, int flags)
148 {
149         ADS_STATUS rc;
150         NTSTATUS status = NT_STATUS_NONE_MAPPED;
151         const char *attrs[] = { "objectSid", NULL };
152         LDAPMessage *res = NULL;
153         LDAPMessage *msg = NULL;
154         char *expr = NULL;
155         fstring sid_string;
156         int count;
157         ADS_STRUCT *ads;
158
159         if (sid == NULL) {
160                 return NT_STATUS_INVALID_PARAMETER;
161         }
162
163         ads = ad_idmap_cached_connection();
164         if (ads == NULL) {
165                 DEBUG(1, ("ad_idmap_get_id_from_sid ADS uninitialized\n"));
166                 return NT_STATUS_NOT_SUPPORTED;
167         }
168
169         switch (id_type) {
170                 case ID_USERID:
171                         if (asprintf(&expr, "(&(|(sAMAccountType=%d)(sAMAccountType=%d)(sAMAccountType=%d))(%s=%d))",
172                                 ATYPE_NORMAL_ACCOUNT, ATYPE_WORKSTATION_TRUST, ATYPE_INTERDOMAIN_TRUST,
173                                 ads->schema.posix_uidnumber_attr, (int)unid.uid) == -1) {
174                                 return NT_STATUS_NO_MEMORY;
175                         }
176                         break;
177                 case ID_GROUPID:
178                         if (asprintf(&expr, "(&(|(sAMAccountType=%d)(sAMAccountType=%d))(%s=%d))",
179                                 ATYPE_SECURITY_GLOBAL_GROUP, ATYPE_SECURITY_LOCAL_GROUP,
180                                 ads->schema.posix_gidnumber_attr, (int)unid.gid) == -1) {
181                                 return NT_STATUS_NO_MEMORY;
182                         }
183                         break;
184                 default:
185                         return NT_STATUS_INVALID_PARAMETER;
186                         break;
187         }
188
189         rc = ads_search_retry(ads, &res, expr, attrs);
190         free(expr);
191         if (!ADS_ERR_OK(rc)) {
192                 DEBUG(1, ("ad_idmap_get_sid_from_id: ads_search: %s\n", ads_errstr(rc)));
193                 goto done;
194         }
195
196         count = ads_count_replies(ads, res);
197         if (count == 0) {
198                 DEBUG(1, ("ad_idmap_get_sid_from_id: ads_count_replies: no results\n"));
199                 goto done;
200         } else if (count != 1) {
201                 DEBUG(1, ("ad_idmap_get_sid_from_id: ads_count_replies: incorrect cardinality\n"));
202                 goto done;
203         }
204
205         msg = ads_first_entry(ads, res);
206         if (msg == NULL) {
207                 DEBUG(1, ("ad_idmap_get_sid_from_id: ads_first_entry: could not retrieve search result\n"));
208                 goto done;
209         }
210
211         if (!ads_pull_sid(ads, msg, "objectSid", sid)) {
212                 DEBUG(1, ("ad_idmap_get_sid_from_id: ads_pull_sid: could not retrieve SID from entry\n"));
213                 goto done;
214         }
215
216         status = NT_STATUS_OK;
217         DEBUG(1, ("ad_idmap_get_sid_from_id mapped POSIX %s %d to SID [%s]\n",
218                 (id_type == ID_GROUPID) ? "GID" : "UID", (int)unid.uid,
219                 sid_to_string(sid_string, sid)));
220
221 done:
222         if (res != NULL) {
223                 ads_msgfree(ads, res);
224         }
225
226         return status;
227 }
228
229 static NTSTATUS ad_idmap_get_id_from_sid(unid_t *unid, enum idmap_type *id_type, const DOM_SID *sid, int flags)
230 {
231         ADS_STATUS rc;
232         NTSTATUS status = NT_STATUS_NONE_MAPPED;
233         const char *attrs[] = { "sAMAccountType", ADS_ATTR_SFU_UIDNUMBER_OID, 
234                                                   ADS_ATTR_SFU_GIDNUMBER_OID, 
235                                                   ADS_ATTR_RFC2307_UIDNUMBER_OID,
236                                                   ADS_ATTR_RFC2307_GIDNUMBER_OID,
237                                                   NULL };
238         LDAPMessage *res = NULL;
239         LDAPMessage *msg = NULL;
240         char *expr = NULL;
241         uint32 atype, uid;
242         char *sidstr;
243         fstring sid_string;
244         int count;
245         ADS_STRUCT *ads;
246
247         if (unid == NULL) {
248                 return NT_STATUS_INVALID_PARAMETER;
249         }
250
251         ads = ad_idmap_cached_connection();
252         if (ads == NULL) {
253                 DEBUG(1, ("ad_idmap_get_id_from_sid ADS uninitialized\n"));
254                 return NT_STATUS_NOT_SUPPORTED;
255         }
256
257         sidstr = sid_binstring(sid);
258         if (asprintf(&expr, "(objectSid=%s)", sidstr) == -1) {
259                 free(sidstr);
260                 return NT_STATUS_NO_MEMORY;
261         }
262
263         rc = ads_search_retry(ads, &res, expr, attrs);
264         free(sidstr);
265         free(expr);
266         if (!ADS_ERR_OK(rc)) {
267                 DEBUG(1, ("ad_idmap_get_id_from_sid: ads_search: %s\n", ads_errstr(rc)));
268                 goto done;
269         }
270
271         count = ads_count_replies(ads, res);
272         if (count == 0) {
273                 DEBUG(1, ("ad_idmap_get_id_from_sid: ads_count_replies: no results\n"));
274                 goto done;
275         } else if (count != 1) {
276                 DEBUG(1, ("ad_idmap_get_id_from_sid: ads_count_replies: incorrect cardinality\n"));
277                 goto done;
278         }
279
280         msg = ads_first_entry(ads, res);
281         if (msg == NULL) {
282                 DEBUG(1, ("ad_idmap_get_id_from_sid: ads_first_entry: could not retrieve search result\n"));
283                 goto done;
284         }
285
286         if (!ads_pull_uint32(ads, msg, "sAMAccountType", &atype)) {
287                 DEBUG(1, ("ad_idmap_get_id_from_sid: ads_pull_uint32: could not read SAM account type\n"));
288                 goto done;
289         }
290
291         switch (atype & 0xF0000000) {
292         case ATYPE_SECURITY_GLOBAL_GROUP:
293         case ATYPE_SECURITY_LOCAL_GROUP:
294                 *id_type = ID_GROUPID;
295                 break;
296         case ATYPE_NORMAL_ACCOUNT:
297         case ATYPE_WORKSTATION_TRUST:
298         case ATYPE_INTERDOMAIN_TRUST:
299                 *id_type = ID_USERID;
300                 break;
301         default:
302                 DEBUG(1, ("ad_idmap_get_id_from_sid: unrecognized SAM account type %08x\n", atype));
303                 goto done;
304                 break;
305         }
306
307         if (!ads_pull_uint32(ads, msg, (*id_type == ID_GROUPID) ? attr_gidnumber : attr_uidnumber, &uid)) {
308                 DEBUG(1, ("ad_idmap_get_id_from_sid: ads_pull_uint32: could not read attribute '%s'\n",
309                         (*id_type == ID_GROUPID) ? attr_gidnumber : attr_uidnumber));
310                 goto done;
311         }
312
313         unid->uid = (uid_t)uid;
314
315         status = NT_STATUS_OK;
316         DEBUG(1, ("ad_idmap_get_id_from_sid mapped SID [%s] to POSIX %s %d\n",
317                 sid_to_string(sid_string, sid),
318                 (*id_type == ID_GROUPID) ? "GID" : "UID", uid));
319
320 done:
321         if (res != NULL) {
322                 ads_msgfree(ads, res);
323         }
324
325         return status;
326
327 }
328
329 static NTSTATUS ad_idmap_set_mapping(const DOM_SID *sid, unid_t id, enum idmap_type id_type)
330 {
331         /* Not supported, and probably won't be... */
332         /* (It's not particularly feasible with a single-master model.) */
333
334         return NT_STATUS_NOT_IMPLEMENTED;
335 }
336
337 static NTSTATUS ad_idmap_close(void)
338 {
339         ADS_STRUCT *ads = ad_idmap_ads;
340
341         if (ads != NULL) {
342                 /* we own this ADS_STRUCT so make sure it goes away */
343                 ads->is_mine = True;
344                 ads_destroy( &ads );
345                 ad_idmap_ads = NULL;
346         }
347
348         SAFE_FREE(attr_uidnumber);
349         SAFE_FREE(attr_gidnumber);
350         
351         return NT_STATUS_OK;
352 }
353
354 static NTSTATUS ad_idmap_allocate_id(unid_t *id, enum idmap_type id_type)
355 {
356         return NT_STATUS_NOT_IMPLEMENTED;
357 }
358
359 static void ad_idmap_status(void)
360 {
361         DEBUG(0, ("AD IDMAP Status not available\n"));
362 }
363
364 static struct idmap_methods ad_methods = {
365         ad_idmap_init,
366         ad_idmap_allocate_id,
367         ad_idmap_get_sid_from_id,
368         ad_idmap_get_id_from_sid,
369         ad_idmap_set_mapping,
370         ad_idmap_close,
371         ad_idmap_status
372 };
373
374
375 /* support for new authentication subsystem */
376 NTSTATUS idmap_ad_init(void)
377 {
378         return smb_register_idmap(SMB_IDMAP_INTERFACE_VERSION, "ad", &ad_methods);
379 }
380