]>
Commit | Line | Data |
---|---|---|
224c7076 A |
1 | /* |
2 | * Copyright (c) 2006, 2007 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include <mach/mach.h> | |
25 | #include <mach/mach_error.h> | |
26 | #include <servers/bootstrap.h> | |
27 | #include <unistd.h> | |
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <stdbool.h> | |
31 | #include <membership.h> | |
32 | #include <pthread.h> | |
33 | #include <errno.h> | |
34 | #include <sys/types.h> | |
35 | #include <sys/stat.h> | |
36 | #include <sys/param.h> | |
37 | #include <uuid/uuid.h> | |
38 | #include <string.h> | |
39 | #include <libkern/OSByteOrder.h> | |
511daa4c | 40 | #include <TargetConditionals.h> |
224c7076 A |
41 | |
42 | #include "dirhelper.h" | |
43 | #include "dirhelper_priv.h" | |
44 | ||
45 | #define BUCKETLEN 2 | |
46 | ||
47 | #define MUTEX_LOCK(x) if(__is_threaded) pthread_mutex_lock(x) | |
48 | #define MUTEX_UNLOCK(x) if(__is_threaded) pthread_mutex_unlock(x) | |
49 | ||
50 | #define ENCODEBITS 6 | |
51 | #define ENCODEDSIZE ((8 * UUID_UID_SIZE + ENCODEBITS - 1) / ENCODEBITS) | |
52 | #define UUID_UID_SIZE (sizeof(uuid_t) + sizeof(uid_t)) | |
53 | ||
54 | extern int __is_threaded; | |
55 | ||
56 | static const mode_t modes[] = { | |
57 | 0, /* unused */ | |
58 | 0700, /* temp */ | |
59 | 0700, /* cache */ | |
60 | }; | |
61 | ||
62 | static const char *subdirs[] = { | |
63 | DIRHELPER_TOP_STR, | |
64 | DIRHELPER_TEMP_STR, | |
65 | DIRHELPER_CACHE_STR, | |
66 | }; | |
67 | ||
68 | static const char encode[] = "+-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; | |
69 | ||
70 | static void | |
71 | encode_uuid_uid(uuid_t uuid, uid_t uid, char *str) | |
72 | { | |
73 | unsigned char buf[UUID_UID_SIZE + 1]; | |
74 | unsigned char *bp = buf; | |
75 | int i = 0; | |
76 | unsigned int n; | |
77 | ||
78 | memcpy(bp, uuid, sizeof(uuid_t)); | |
79 | uid = OSSwapHostToBigInt32(uid); | |
80 | memcpy(bp + sizeof(uuid_t), &uid, sizeof(uid_t)); | |
81 | bp[UUID_UID_SIZE] = 0; // this ensures the last encoded byte will have trailing zeros | |
82 | while(i < ENCODEDSIZE) { | |
83 | switch(i % 4) { | |
84 | case 0: | |
85 | n = *bp++; | |
86 | *str++ = encode[n >> 2]; | |
87 | break; | |
88 | case 1: | |
89 | n = ((n & 0x3) << 8) | *bp++; | |
90 | *str++ = encode[n >> 4]; | |
91 | break; | |
92 | case 2: | |
93 | n = ((n & 0xf) << 8) | *bp++; | |
94 | *str++ = encode[n >> 6]; | |
95 | break; | |
96 | case 3: | |
97 | *str++ = encode[n & 0x3f]; | |
98 | break; | |
99 | } | |
100 | i++; | |
101 | } | |
102 | *str = 0; | |
103 | } | |
104 | ||
105 | char * | |
106 | __user_local_dirname(uid_t uid, dirhelper_which_t which, char *path, size_t pathlen) | |
107 | { | |
511daa4c A |
108 | #if TARGET_OS_EMBEDDED |
109 | char *tmpdir; | |
110 | #else | |
224c7076 A |
111 | uuid_t uuid; |
112 | char str[ENCODEDSIZE + 1]; | |
511daa4c | 113 | #endif |
224c7076 A |
114 | int res; |
115 | ||
116 | if(which < 0 || which > DIRHELPER_USER_LOCAL_LAST) { | |
117 | errno = EINVAL; | |
118 | return NULL; | |
119 | } | |
120 | ||
511daa4c A |
121 | #if TARGET_OS_EMBEDDED |
122 | tmpdir = getenv("TMPDIR"); | |
123 | if(!tmpdir) { | |
124 | errno = EINVAL; | |
125 | return NULL; | |
126 | } | |
127 | ||
128 | res = snprintf(path, pathlen, "%s/%s", tmpdir, subdirs[which]); | |
129 | #else | |
224c7076 A |
130 | res = mbr_uid_to_uuid(uid, uuid); |
131 | if(res != 0) { | |
132 | errno = res; | |
133 | return NULL; | |
134 | } | |
135 | ||
136 | // | |
137 | // We partition the namespace so that we don't end up with too | |
138 | // many users in a single directory. With 4096 buckets, we | |
139 | // could scale to 1,000,000 users while keeping the average | |
140 | // number of files in a single directory below 250 | |
141 | // | |
142 | encode_uuid_uid(uuid, uid, str); | |
143 | res = snprintf(path, pathlen, | |
144 | "%s%.*s/%s/%s", | |
145 | VAR_FOLDERS_PATH, BUCKETLEN, str, str, subdirs[which]); | |
511daa4c | 146 | #endif |
224c7076 A |
147 | if(res >= pathlen) { |
148 | errno = EINVAL; | |
149 | return NULL; /* buffer too small */ | |
150 | } | |
151 | return path; | |
152 | } | |
153 | ||
34e8f829 A |
154 | char * |
155 | __user_local_mkdir_p(char *path) | |
156 | { | |
157 | char *next; | |
158 | int res; | |
159 | ||
160 | next = path + strlen(VAR_FOLDERS_PATH); | |
161 | while ((next = strchr(next, '/')) != NULL) { | |
162 | *next = 0; // temporarily truncate | |
163 | res = mkdir(path, 0755); | |
164 | if (res != 0 && errno != EEXIST) | |
165 | return NULL; | |
166 | *next++ = '/'; // restore the slash and increment | |
167 | } | |
168 | return path; | |
169 | } | |
170 | ||
224c7076 A |
171 | __private_extern__ char * |
172 | _dirhelper(dirhelper_which_t which, char *path, size_t pathlen) | |
173 | { | |
174 | static char userdir[PATH_MAX]; | |
175 | static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; | |
176 | int res; | |
177 | struct stat sb; | |
178 | ||
179 | if(which < 0 || which > DIRHELPER_USER_LOCAL_LAST) { | |
180 | errno = EINVAL; | |
181 | return NULL; | |
182 | } | |
183 | ||
184 | if(!*userdir) { | |
185 | MUTEX_LOCK(&lock); | |
34e8f829 A |
186 | if (!*userdir) { |
187 | ||
224c7076 | 188 | if(__user_local_dirname(geteuid(), DIRHELPER_USER_LOCAL, userdir, sizeof(userdir)) == NULL) { |
224c7076 A |
189 | MUTEX_UNLOCK(&lock); |
190 | return NULL; | |
191 | } | |
192 | /* | |
34e8f829 A |
193 | * check if userdir exists, and if not, either do the work |
194 | * ourself if we are root, or call | |
195 | * __dirhelper_create_user_local to create it (we have to | |
196 | * check again afterwards). | |
224c7076 A |
197 | */ |
198 | if(stat(userdir, &sb) < 0) { | |
34e8f829 A |
199 | mach_port_t mp; |
200 | ||
224c7076 A |
201 | if(errno != ENOENT) { /* some unknown error */ |
202 | *userdir = 0; | |
34e8f829 A |
203 | MUTEX_UNLOCK(&lock); |
204 | return NULL; | |
224c7076 | 205 | } |
34e8f829 A |
206 | /* |
207 | * If we are root, lets do what dirhelper does for us. | |
208 | */ | |
209 | if (geteuid() == 0) { | |
210 | if (__user_local_mkdir_p(userdir) == NULL) { | |
211 | *userdir = 0; | |
212 | MUTEX_UNLOCK(&lock); | |
213 | return NULL; | |
214 | } | |
215 | } else { | |
216 | if(bootstrap_look_up(bootstrap_port, DIRHELPER_BOOTSTRAP_NAME, &mp) != KERN_SUCCESS) { | |
217 | errno = EPERM; | |
218 | server_error: | |
219 | mach_port_deallocate(mach_task_self(), mp); | |
220 | MUTEX_UNLOCK(&lock); | |
221 | return NULL; | |
222 | } | |
223 | if(__dirhelper_create_user_local(mp) != KERN_SUCCESS) { | |
224 | errno = EPERM; | |
225 | goto server_error; | |
226 | } | |
227 | /* double check that the directory really got created */ | |
228 | if(stat(userdir, &sb) < 0) { | |
229 | goto server_error; | |
230 | } | |
231 | mach_port_deallocate(mach_task_self(), mp); | |
224c7076 A |
232 | } |
233 | } | |
224c7076 A |
234 | } |
235 | MUTEX_UNLOCK(&lock); | |
236 | } | |
237 | ||
238 | if(pathlen < strlen(userdir) + strlen(subdirs[which]) + 1) { | |
239 | errno = EINVAL; | |
240 | return NULL; /* buffer too small */ | |
241 | } | |
242 | strcpy(path, userdir); | |
243 | strcat(path, subdirs[which]); | |
244 | ||
245 | /* | |
246 | * now for subdirectories, create it with the appropriate permissions | |
247 | * if it doesn't already exist. | |
248 | */ | |
249 | if(which != DIRHELPER_USER_LOCAL) { | |
250 | res = mkdir(path, modes[which]); | |
251 | if(res != 0 && errno != EEXIST) | |
252 | return NULL; | |
253 | } | |
254 | ||
255 | return path; | |
256 | } |