1/* Copyright (c) 2012 Apple Inc. All rights reserved. */
2
3#include "credential.h"
4#include "authutilities.h"
5#include "debugging.h"
6#include "crc.h"
7
8#include <pwd.h>
9#include <membership.h>
10#include <membershipPriv.h>
11
12struct _credential_s {
13    __AUTH_BASE_STRUCT_HEADER__;
14
15    bool right;   // is least-privileged credential
16
17    uid_t uid;
18    char * name;
19    char * realName;
20
21    CFAbsoluteTime creationTime;
22    bool valid;
23    bool shared;
24
25    CFMutableSetRef cachedGroups;
26};
27
28static void
29_credential_finalize(CFTypeRef value)
30{
31    credential_t cred = (credential_t)value;
32
33    free_safe(cred->name);
34    free_safe(cred->realName);
35    CFReleaseSafe(cred->cachedGroups);
36}
37
38static CFStringRef
39_credential_copy_description(CFTypeRef value)
40{
41    credential_t cred = (credential_t)value;
42    CFStringRef str = NULL;
43    CFTimeZoneRef sys_tz = CFTimeZoneCopySystem();
44    CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(cred->creationTime, sys_tz);
45    CFReleaseSafe(sys_tz);
46    if (cred->right) {
47        str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("credential: right=%s, shared=%i, creation=%01i:%01i:%01i, valid=%i"), cred->name, cred->shared, date.hour,date.minute,(int32_t)date.second, cred->valid);
48    } else {
49        str = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("credential: uid=%i, name=%s, shared=%i, creation=%01i:%01i:%01i valid=%i"), cred->uid, cred->name, cred->shared, date.hour,date.minute,(int32_t)date.second, cred->valid);
50    }
51    return str;
52}
53
54static CFHashCode
55_credential_hash(CFTypeRef value)
56{
57    credential_t cred = (credential_t)value;
58    uint64_t crc = crc64_init();
59    if (cred->right) {
60        crc = crc64_update(crc, cred->name, strlen(cred->name));
61    } else {
62        crc = crc64_update(crc, &cred->uid, sizeof(cred->uid));
63    }
64    crc = crc64_update(crc, &cred->shared, sizeof(cred->shared));
65    crc = crc64_final(crc);
66
67    return crc;
68}
69
70static Boolean
71_credential_equal(CFTypeRef value1, CFTypeRef value2)
72{
73    credential_t cred1 = (credential_t)value1;
74    credential_t cred2 = (credential_t)value2;
75
76    return _credential_hash(cred1) == _credential_hash(cred2);
77}
78
79AUTH_TYPE_INSTANCE(credential,
80                   .init = NULL,
81                   .copy = NULL,
82                   .finalize = _credential_finalize,
83                   .equal = _credential_equal,
84                   .hash = _credential_hash,
85                   .copyFormattingDesc = NULL,
86                   .copyDebugDesc = _credential_copy_description
87                   );
88
89static CFTypeID credential_get_type_id() {
90    static CFTypeID type_id = _kCFRuntimeNotATypeID;
91    static dispatch_once_t onceToken;
92
93    dispatch_once(&onceToken, ^{
94        type_id = _CFRuntimeRegisterClass(&_auth_type_credential);
95    });
96
97    return type_id;
98}
99
100static credential_t
101_credential_create()
102{
103    credential_t cred = NULL;
104
105    cred = (credential_t)_CFRuntimeCreateInstance(kCFAllocatorDefault, credential_get_type_id(), AUTH_CLASS_SIZE(credential), NULL);
106    require(cred != NULL, done);
107
108    cred->creationTime = CFAbsoluteTimeGetCurrent();
109    cred->cachedGroups = CFSetCreateMutable(kCFAllocatorDefault, 0, &kCFTypeSetCallBacks);;
110
111done:
112    return cred;
113}
114
115credential_t
116credential_create(uid_t uid)
117{
118    credential_t cred = NULL;
119
120    cred = _credential_create();
121    require(cred != NULL, done);
122
123    struct passwd *pw = getpwuid(uid);
124	if (pw != NULL) {
125        // avoid hinting a locked account
126		if ( (pw->pw_passwd == NULL) || strcmp(pw->pw_passwd, "*") ) {
127            cred->uid = pw->pw_uid;
128            cred->name = _copy_string(pw->pw_name);
129            cred->realName = _copy_string(pw->pw_gecos);
130            cred->valid = true;
131        } else {
132            cred->uid = (uid_t)-2;
133            cred->valid = false;
134        }
135        endpwent();
136    }
137
138done:
139    return cred;
140}
141
142credential_t
143credential_create_with_credential(credential_t srcCred, bool shared)
144{
145    credential_t cred = NULL;
146
147    cred = _credential_create();
148    require(cred != NULL, done);
149
150    cred->uid = srcCred->uid;
151    cred->name = _copy_string(srcCred->name);
152    cred->realName = _copy_string(srcCred->realName);
153    cred->valid = srcCred->valid;
154    cred->right = srcCred->right;
155    cred->shared = shared;
156
157done:
158    return cred;
159}
160
161credential_t
162credential_create_with_right(const char * right)
163{
164    credential_t cred = NULL;
165
166    cred = _credential_create();
167    require(cred != NULL, done);
168
169    cred->right = true;
170    cred->name = _copy_string(right);
171    cred->uid = (uid_t)-2;
172    cred->valid = true;
173
174done:
175    return cred;
176}
177
178uid_t
179credential_get_uid(credential_t cred)
180{
181    return cred->uid;
182}
183
184const char *
185credential_get_name(credential_t cred)
186{
187    return cred->name;
188}
189
190const char *
191credential_get_realname(credential_t cred)
192{
193    return cred->realName;
194}
195
196CFAbsoluteTime
197credential_get_creation_time(credential_t cred)
198{
199    return cred->creationTime;
200}
201
202bool
203credential_get_valid(credential_t cred)
204{
205    return cred->valid;
206}
207
208bool
209credential_get_shared(credential_t cred)
210{
211    return cred->shared;
212}
213
214bool
215credential_is_right(credential_t cred)
216{
217    return cred->right;
218}
219
220bool
221credential_check_membership(credential_t cred,const char* group)
222{
223    bool result = false;
224    CFStringRef cachedGroup = NULL;
225    require(group != NULL, done);
226    require(cred->uid != 0 || cred->uid != (uid_t)-2, done);
227    require(cred->right != true, done);
228
229    cachedGroup = CFStringCreateWithCString(kCFAllocatorDefault, group, kCFStringEncodingUTF8);
230    require(cachedGroup != NULL, done);
231
232    if (CFSetGetValue(cred->cachedGroups, cachedGroup) != NULL) {
233        result = true;
234        goto done;
235    }
236
237    int rc, ismember;
238    uuid_t group_uuid, user_uuid;
239    rc = mbr_group_name_to_uuid(group, group_uuid);
240    require_noerr(rc, done);
241
242    rc = mbr_uid_to_uuid(cred->uid, user_uuid);
243    require_noerr(rc, done);
244
245    rc = mbr_check_membership(user_uuid, group_uuid, &ismember);
246    require_noerr(rc, done);
247
248    result = ismember;
249
250    if (ismember) {
251        CFSetSetValue(cred->cachedGroups, cachedGroup);
252    }
253
254done:
255    CFReleaseSafe(cachedGroup);
256    return result;
257}
258
259void
260credential_invalidate(credential_t cred)
261{
262    cred->valid = false;
263}
264