1/*
2 * Copyright (c) 2001-2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23
24#include <CoreFoundation/CoreFoundation.h>
25
26#include <DirectoryService/DirServices.h>
27#include <DirectoryService/DirServicesUtils.h>
28#include <DirectoryService/DirServicesConst.h>
29#include <CoreFoundation/CFString.h>
30#include <SystemConfiguration/SystemConfiguration.h>
31
32#include "vmbuf.h"
33#include "remoteconf.h"
34#include "plog.h"
35#include "misc.h"
36#include "gcmalloc.h"
37#include "open_dir.h"
38
39#define BUF_LEN 		1024
40
41
42static tDirStatus open_dir_get_search_node_ref (tDirReference dirRef, unsigned long index,
43                tDirNodeReference *searchNodeRef, unsigned long *count);
44static tDirStatus open_dir_get_user_attr (tDirReference dirRef, tDirNodeReference searchNodeRef, char *user_name,
45                char *attr, tAttributeValueEntryPtr *attr_value);
46static tDirStatus open_dir_check_group_membership (tDirReference dirRef, tDirNodeReference searchNodeRef,
47                char *group_name, char *user_name, char *userGID, int *authorized);
48
49
50//----------------------------------------------------------------------
51//	open_dir_authorize_id
52//----------------------------------------------------------------------
53int open_dir_authorize_id(vchar_t *id, vchar_t *group)
54{
55
56    tDirReference 			dirRef;
57    tDirStatus 				dsResult = eDSNoErr;
58    int						authorized = 0;
59    tDirNodeReference 		searchNodeRef;
60    tAttributeValueEntryPtr	groupID = NULL;
61    tAttributeValueEntryPtr	recordName = NULL;
62    unsigned long 			searchNodeCount;
63    char*					user_name = NULL;
64    char*					group_name = NULL;
65
66    if (id == 0 || id->l < 1) {
67        plog(ASL_LEVEL_ERR, "invalid user name.\n");
68        goto end;
69    }
70   	user_name = racoon_malloc(id->l + 1);
71   	if (user_name == NULL) {
72   		plog(ASL_LEVEL_ERR, "out of memory - unable to allocate space for user name.\n");
73   		goto end;
74   	}
75   	bcopy(id->v, user_name, id->l);
76   	*(user_name + id->l) = 0;
77
78   	if (group && group->l > 0) {
79   		group_name = racoon_malloc(group->l + 1);
80   		if (group_name == NULL) {
81   			plog(ASL_LEVEL_ERR, "out of memeory - unable to allocate space for group name.\n");
82   			goto end;
83   		}
84   		bcopy(group->v, group_name, group->l);
85   		*(group_name + group->l) = 0;
86   	}
87
88    if ((dsResult = dsOpenDirService(&dirRef)) == eDSNoErr) {
89        // get the search node ref
90        if ((dsResult = open_dir_get_search_node_ref(dirRef, 1, &searchNodeRef, &searchNodeCount)) == eDSNoErr) {
91            // get the user's primary group ID
92           	if ((dsResult = open_dir_get_user_attr(dirRef, searchNodeRef, user_name, kDSNAttrRecordName, &recordName)) == eDSNoErr) {
93           		if (recordName != 0) {
94           			if (group_name != 0) {
95						if ((dsResult = open_dir_get_user_attr(dirRef, searchNodeRef, user_name, kDS1AttrPrimaryGroupID, &groupID)) == eDSNoErr) {
96							// check if user is member of the group
97							dsResult = open_dir_check_group_membership(dirRef, searchNodeRef, group_name,
98								recordName->fAttributeValueData.fBufferData, groupID->fAttributeValueData.fBufferData, &authorized);
99						}
100					} else
101						authorized = 1;	// no group required - user record found
102 				}
103            }
104            if (groupID)
105                dsDeallocAttributeValueEntry(dirRef, groupID);
106            if (recordName)
107            	dsDeallocAttributeValueEntry(dirRef, recordName);
108            dsCloseDirNode(searchNodeRef);		// close the search node
109        }
110        dsCloseDirService(dirRef);
111    }
112
113end:
114    if (authorized)
115        plog(ASL_LEVEL_NOTICE, "User '%s' authorized for access\n", user_name);
116    else
117        plog(ASL_LEVEL_NOTICE, "User '%s' not authorized for access\n", user_name);
118    if (user_name)
119    	free(user_name);
120    if (group_name)
121    	free(group_name);
122    return authorized;
123}
124
125
126//----------------------------------------------------------------------
127//	open_dir_get_search_node_ref
128//----------------------------------------------------------------------
129static tDirStatus open_dir_get_search_node_ref(tDirReference dirRef, unsigned long index,
130                tDirNodeReference *searchNodeRef, unsigned long *count)
131{
132    tDirStatus			dsResult = -1;
133    tDataBufferPtr		searchNodeDataBufferPtr = 0;
134    tDataListPtr	   	searchNodeNameDataListPtr = 0;
135
136    unsigned long		outNodeCount;
137    tContextData		continueData = 0;
138
139    *searchNodeRef = 0;
140    *count = 0;
141
142    // allocate required buffers and data lists
143    if ((searchNodeDataBufferPtr = dsDataBufferAllocate(dirRef, BUF_LEN)) == 0) {
144        plog(ASL_LEVEL_ERR, "Could not allocate tDataBuffer\n");
145        goto cleanup;
146    }
147    if ((searchNodeNameDataListPtr = dsDataListAllocate(dirRef)) == 0) {
148        plog(ASL_LEVEL_ERR, "Could not allocate tDataList\n");
149        goto cleanup;
150    }
151
152    // find authentication search node(s)
153    if ((dsResult = dsFindDirNodes(dirRef, searchNodeDataBufferPtr, 0, eDSAuthenticationSearchNodeName,
154                                                                (UInt32*)&outNodeCount, &continueData)) == eDSNoErr) {
155        if (outNodeCount != 0) {
156
157            // get the seach node name and open the node
158            if ((dsResult = dsGetDirNodeName(dirRef, searchNodeDataBufferPtr, index,
159                                                        &searchNodeNameDataListPtr)) == eDSNoErr) {
160                if ((dsResult = dsOpenDirNode(dirRef, searchNodeNameDataListPtr, searchNodeRef)) == eDSNoErr) {
161                    *count = outNodeCount;
162                }
163            }
164        }
165        if (continueData)
166            dsReleaseContinueData(dirRef, continueData);
167    }
168
169cleanup:
170    if (searchNodeDataBufferPtr)
171        dsDataBufferDeAllocate(dirRef, searchNodeDataBufferPtr);
172    if (searchNodeNameDataListPtr)
173        dsDataListDeallocate(dirRef, searchNodeNameDataListPtr);
174
175    return dsResult;
176}
177
178//----------------------------------------------------------------------
179//	open_dir_get_user_attr
180//----------------------------------------------------------------------
181static tDirStatus open_dir_get_user_attr(tDirReference dirRef, tDirNodeReference searchNodeRef, char *user_name,
182                char *attr, tAttributeValueEntryPtr *attr_value)
183{
184
185    tDirStatus			dsResult = -1;
186
187    tDataBufferPtr		userRcdDataBufferPtr = 0;
188    tDataListPtr	   	recordNameDataListPtr = 0;
189    tDataListPtr	   	recordTypeDataListPtr = 0;
190    tDataListPtr	   	attrTypeDataListPtr = 0;
191    tContextData		continueData = 0;
192
193    unsigned long		outRecordCount;
194    int				userRcdFound = 0;
195    u_int32_t			userRecordIndex, attrIndex;
196
197    *attr_value	= 0;
198
199    if ((userRcdDataBufferPtr = dsDataBufferAllocate(dirRef, BUF_LEN)) == 0) {
200        plog(ASL_LEVEL_ERR, "Could not allocate tDataBuffer\n");
201        goto cleanup;
202    }
203    if ((recordNameDataListPtr = dsBuildListFromStrings(dirRef, user_name, 0)) == 0) {
204        plog(ASL_LEVEL_ERR, "Could not allocate tDataList\n");
205        goto cleanup;
206    }
207    if ((recordTypeDataListPtr = dsBuildListFromStrings(dirRef, kDSStdRecordTypeUsers, 0)) == 0) {
208        plog(ASL_LEVEL_ERR, "Could not allocate tDataList\n");
209        goto cleanup;
210    }
211    if ((attrTypeDataListPtr = dsBuildListFromStrings(dirRef, kDSNAttrRecordName, kDS1AttrDistinguishedName, attr, 0)) == 0) {
212        plog(ASL_LEVEL_ERR, "Could not allocate tDataList\n");
213        goto cleanup;
214    }
215
216    // find the user record(s), extracting the user name and requested attribute
217    do {
218        dsResult = dsGetRecordList(searchNodeRef, userRcdDataBufferPtr, recordNameDataListPtr, eDSExact,
219                    recordTypeDataListPtr, attrTypeDataListPtr, 0, (UInt32*)&outRecordCount, &continueData);
220
221        // if buffer too small - allocate a larger one
222        if (dsResult == eDSBufferTooSmall) {
223            u_int32_t	size = userRcdDataBufferPtr->fBufferSize * 2;
224
225            dsDataBufferDeAllocate(dirRef, userRcdDataBufferPtr);
226            if ((userRcdDataBufferPtr = dsDataBufferAllocate(dirRef, size)) == 0) {
227                plog(ASL_LEVEL_ERR, "Could not allocate tDataBuffer\n");
228		dsResult = -1;
229		goto cleanup;
230            }
231        }
232    } while (dsResult == eDSBufferTooSmall);
233
234    if (dsResult == eDSNoErr) {
235        // for each user record
236        for (userRecordIndex = 1; (userRecordIndex <= outRecordCount) && (dsResult == eDSNoErr)
237                                                        && (userRcdFound == 0); userRecordIndex++) {
238
239            tAttributeListRef	attrListRef;
240            tRecordEntryPtr	userRcdEntryPtr;
241
242            // get the user record entry from the data buffer
243            if ((dsResult = dsGetRecordEntry(searchNodeRef, userRcdDataBufferPtr, userRecordIndex,
244                                                    &attrListRef, &userRcdEntryPtr)) == eDSNoErr) {
245                // for each attribute
246                for (attrIndex = 1; (attrIndex <= userRcdEntryPtr->fRecordAttributeCount)
247                                                            && (dsResult == eDSNoErr); attrIndex++) {
248
249                    tAttributeValueListRef	attrValueListRef;
250                    tAttributeEntryPtr		attrInfoPtr;
251                    tAttributeValueEntryPtr	attrValuePtr;
252
253                    if ((dsResult = dsGetAttributeEntry(searchNodeRef, userRcdDataBufferPtr,
254                                        attrListRef, attrIndex, &attrValueListRef, &attrInfoPtr)) == eDSNoErr) {
255                        if ((dsResult = dsGetAttributeValue(searchNodeRef, userRcdDataBufferPtr, 1,
256                                        attrValueListRef, &attrValuePtr)) == eDSNoErr) {
257
258                            // check for user record name or attribute searching for
259                            if (!strcmp(attrInfoPtr->fAttributeSignature.fBufferData, kDSNAttrRecordName)) {
260                                if (!strcmp(attrValuePtr->fAttributeValueData.fBufferData, user_name))
261                                    userRcdFound = 1;
262                            }
263                            if (!strcmp(attrInfoPtr->fAttributeSignature.fBufferData, kDS1AttrDistinguishedName)) {
264                                if (!strcmp(attrValuePtr->fAttributeValueData.fBufferData, user_name))
265                                    userRcdFound = 1;
266                            }
267                            if (!strcmp(attrInfoPtr->fAttributeSignature.fBufferData, attr)) {
268                                *attr_value = attrValuePtr;	// return the attribute value
269                                attrValuePtr = 0;		// set to zero so we don't deallocate it
270                            }
271                            if (attrValuePtr)
272                                dsDeallocAttributeValueEntry(dirRef, attrValuePtr);
273                        }
274                        dsCloseAttributeValueList(attrValueListRef);
275                        dsDeallocAttributeEntry(dirRef, attrInfoPtr);
276                    }
277                }
278                // make sure we've processed both attributes and we have a match on user name
279                if(userRcdFound == 0 || *attr_value == 0) {
280                    userRcdFound = 0;
281                    if (*attr_value)
282                    	dsDeallocAttributeValueEntry(dirRef, *attr_value);
283                    *attr_value = 0;
284                }
285                dsCloseAttributeList(attrListRef);
286                dsDeallocRecordEntry(dirRef, userRcdEntryPtr);
287            }
288        }
289    }
290
291cleanup:
292	if (continueData)
293		dsReleaseContinueData(searchNodeRef, continueData);
294    if (userRcdDataBufferPtr)
295        dsDataBufferDeAllocate(dirRef, userRcdDataBufferPtr);
296    if (recordNameDataListPtr)
297        dsDataListDeallocate(dirRef, recordNameDataListPtr);
298    if (recordTypeDataListPtr)
299        dsDataListDeallocate(dirRef, recordTypeDataListPtr);
300    if (attrTypeDataListPtr)
301        dsDataListDeallocate(dirRef, attrTypeDataListPtr);
302
303    return dsResult;
304
305}
306
307
308//----------------------------------------------------------------------
309//	open_dir_check_group_membership
310//----------------------------------------------------------------------
311static tDirStatus open_dir_check_group_membership(tDirReference dirRef, tDirNodeReference searchNodeRef,
312                char *group_name, char *user_name, char *userGID, int *authorized)
313{
314    tDirStatus			dsResult = -1;
315
316    tDataBufferPtr		groupRcdDataBufferPtr = 0;
317    tDataListPtr	   	recordNameDataListPtr = 0;
318    tDataListPtr	   	recordTypeDataListPtr = 0;
319    tDataListPtr	   	attrTypeDataListPtr = 0;
320    tContextData		continueData = 0;
321
322    unsigned long		outRecordCount;
323    u_int32_t			attrIndex, valueIndex;
324
325    *authorized	= 0;
326
327    if ((groupRcdDataBufferPtr = dsDataBufferAllocate(dirRef, BUF_LEN)) == 0) {
328        plog(ASL_LEVEL_ERR, "Could not allocate tDataBuffer\n");
329        goto cleanup;
330    }
331    if ((recordNameDataListPtr = dsBuildListFromStrings(dirRef, group_name, 0)) == 0) {
332        plog(ASL_LEVEL_ERR, "Could not allocate tDataList\n");
333        goto cleanup;
334    }
335    if ((recordTypeDataListPtr = dsBuildListFromStrings(dirRef, kDSStdRecordTypeGroups, 0)) == 0) {
336        plog(ASL_LEVEL_ERR, "Could not allocate tDataList\n");
337        goto cleanup;
338    }
339    if ((attrTypeDataListPtr = dsBuildListFromStrings(dirRef, kDS1AttrPrimaryGroupID, kDSNAttrGroupMembership, 0)) == 0) {
340        plog(ASL_LEVEL_ERR, "Could not allocate tDataList\n");
341        goto cleanup;
342    }
343
344    // find the group record, extracting the group ID and group membership attribute
345    do {
346        dsResult = dsGetRecordList(searchNodeRef, groupRcdDataBufferPtr, recordNameDataListPtr, eDSExact,
347                            recordTypeDataListPtr, attrTypeDataListPtr, 0, (UInt32*)&outRecordCount, &continueData);
348        // if buffer too small - allocate a larger one
349        if (dsResult == eDSBufferTooSmall) {
350            u_int32_t	size = groupRcdDataBufferPtr->fBufferSize * 2;
351
352            dsDataBufferDeAllocate(dirRef, groupRcdDataBufferPtr);
353            if ((groupRcdDataBufferPtr = dsDataBufferAllocate(dirRef, size)) == 0) {
354                plog(ASL_LEVEL_ERR, "Could not allocate tDataBuffer\n");
355                dsResult = -1;
356                goto cleanup;
357            }
358        }
359    } while (dsResult == eDSBufferTooSmall);
360
361    if (dsResult == eDSNoErr) {
362
363        tAttributeListRef	attrListRef;
364        tRecordEntryPtr		groupRcdEntryPtr;
365
366        // get the group record entry
367        if ((dsResult = dsGetRecordEntry(searchNodeRef, groupRcdDataBufferPtr, 1, &attrListRef, &groupRcdEntryPtr)) == eDSNoErr) {
368
369            // for each attribute
370            for (attrIndex = 1; (attrIndex <= groupRcdEntryPtr->fRecordAttributeCount) && (dsResult == eDSNoErr)
371                                    && (*authorized == 0); attrIndex++) {
372
373                tAttributeValueListRef	attrValueListRef;
374                tAttributeEntryPtr	attrInfoPtr;
375                tAttributeValueEntryPtr	attrValuePtr;
376
377                if ((dsResult = dsGetAttributeEntry(searchNodeRef, groupRcdDataBufferPtr, attrListRef,
378                                                        attrIndex, &attrValueListRef, &attrInfoPtr)) == eDSNoErr) {
379
380                    // group ID attribute ?
381                    if (!strcmp(attrInfoPtr->fAttributeSignature.fBufferData, kDS1AttrPrimaryGroupID)) {
382                    	if ((dsResult = dsGetAttributeValue(searchNodeRef, groupRcdDataBufferPtr, 1,
383                                                            attrValueListRef, &attrValuePtr)) == eDSNoErr) {
384
385                            // check for match on primary group ID
386                            if (!strcmp(attrValuePtr->fAttributeValueData.fBufferData, userGID))
387                                *authorized = 1;
388                            dsDeallocAttributeValueEntry(dirRef, attrValuePtr);
389                        }
390                    } else if (!strcmp(attrInfoPtr->fAttributeSignature.fBufferData, kDSNAttrGroupMembership)) {
391                        // for each value check for user's name in the group
392                        for (valueIndex = 1; (valueIndex <= attrInfoPtr->fAttributeValueCount)
393                                                && (dsResult == eDSNoErr) && (*authorized == 0); valueIndex++) {
394
395                            if ((dsResult = dsGetAttributeValue(searchNodeRef, groupRcdDataBufferPtr,
396                                                    valueIndex, attrValueListRef, &attrValuePtr)) == eDSNoErr) {
397                                if (!strcmp(attrValuePtr->fAttributeValueData.fBufferData, user_name))
398                                    *authorized = 1;
399                                dsDeallocAttributeValueEntry(dirRef, attrValuePtr);
400                            }
401                        }
402                    }
403                    dsCloseAttributeValueList(attrValueListRef);
404                    dsDeallocAttributeEntry(dirRef, attrInfoPtr);
405                }
406            }
407            dsCloseAttributeList(attrListRef);
408            dsDeallocRecordEntry(dirRef, groupRcdEntryPtr);
409        }
410    }
411
412cleanup:
413	if (continueData)
414		dsReleaseContinueData(searchNodeRef, continueData);
415    if (groupRcdDataBufferPtr)
416        dsDataBufferDeAllocate(dirRef, groupRcdDataBufferPtr);
417    if (recordNameDataListPtr)
418        dsDataListDeallocate(dirRef, recordNameDataListPtr);
419    if (recordTypeDataListPtr)
420        dsDataListDeallocate(dirRef, recordTypeDataListPtr);
421    if (attrTypeDataListPtr)
422        dsDataListDeallocate(dirRef, attrTypeDataListPtr);
423
424    return dsResult;
425}
426
427