1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 */
24
25 #ifndef _KERNEL
26 #include <stdio.h>
27 #include <strings.h>
28 #include <stdlib.h>
29 #include <syslog.h>
30 #include <smbsrv/libsmb.h>
31 #else /* _KERNEL */
32 #include <sys/types.h>
33 #include <sys/sunddi.h>
34 #endif /* _KERNEL */
35
36 #include <smbsrv/smb_sid.h>
37
38 static smb_sid_t *smb_sid_alloc(size_t);
39
40 /*
41 * smb_sid_isvalid
42 *
43 * Performs a minimal SID validation.
44 */
45 boolean_t
smb_sid_isvalid(smb_sid_t * sid)46 smb_sid_isvalid(smb_sid_t *sid)
47 {
48 if (sid == NULL)
49 return (B_FALSE);
50
51 return ((sid->sid_revision == NT_SID_REVISION) &&
52 (sid->sid_subauthcnt < NT_SID_SUBAUTH_MAX));
53 }
54
55 /*
56 * smb_sid_len
57 *
58 * Returns the number of bytes required to hold the sid.
59 */
60 int
smb_sid_len(smb_sid_t * sid)61 smb_sid_len(smb_sid_t *sid)
62 {
63 if (sid == NULL)
64 return (0);
65
66 return (sizeof (smb_sid_t) - sizeof (uint32_t)
67 + (sid->sid_subauthcnt * sizeof (uint32_t)));
68 }
69
70 /*
71 * smb_sid_dup
72 *
73 * Make a duplicate of the specified sid. The memory for the new sid
74 * should be freed by calling smb_sid_free().
75 * A pointer to the new sid is returned.
76 */
77 smb_sid_t *
smb_sid_dup(smb_sid_t * sid)78 smb_sid_dup(smb_sid_t *sid)
79 {
80 smb_sid_t *new_sid;
81 int size;
82
83 if (sid == NULL)
84 return (NULL);
85
86 size = smb_sid_len(sid);
87 if ((new_sid = smb_sid_alloc(size)) == NULL)
88 return (NULL);
89
90 bcopy(sid, new_sid, size);
91 return (new_sid);
92 }
93
94
95 /*
96 * smb_sid_splice
97 *
98 * Make a full sid from a domain sid and a relative id (rid).
99 * The memory for the result sid should be freed by calling
100 * smb_sid_free(). A pointer to the new sid is returned.
101 */
102 smb_sid_t *
smb_sid_splice(smb_sid_t * domain_sid,uint32_t rid)103 smb_sid_splice(smb_sid_t *domain_sid, uint32_t rid)
104 {
105 smb_sid_t *sid;
106 int size;
107
108 if (domain_sid == NULL)
109 return (NULL);
110
111 size = smb_sid_len(domain_sid);
112 if ((sid = smb_sid_alloc(size + sizeof (rid))) == NULL)
113 return (NULL);
114
115 bcopy(domain_sid, sid, size);
116
117 sid->sid_subauth[domain_sid->sid_subauthcnt] = rid;
118 ++sid->sid_subauthcnt;
119
120 return (sid);
121 }
122
123 /*
124 * smb_sid_getrid
125 *
126 * Return the Relative Id (RID) of the specified SID. It is the
127 * caller's responsibility to ensure that this is an appropriate SID.
128 * All we do here is return the last sub-authority from the SID.
129 */
130 int
smb_sid_getrid(smb_sid_t * sid,uint32_t * rid)131 smb_sid_getrid(smb_sid_t *sid, uint32_t *rid)
132 {
133 if (!smb_sid_isvalid(sid) || (rid == NULL) ||
134 (sid->sid_subauthcnt == 0))
135 return (-1);
136
137 *rid = sid->sid_subauth[sid->sid_subauthcnt - 1];
138 return (0);
139 }
140
141 /*
142 * smb_sid_split
143 *
144 * Take a full sid and split it into a domain sid and a relative id (rid).
145 * The domain SID is allocated and a pointer to it will be returned. The
146 * RID value is passed back in 'rid' arg if it's not NULL. The allocated
147 * memory for the domain SID must be freed by caller.
148 */
149 smb_sid_t *
smb_sid_split(smb_sid_t * sid,uint32_t * rid)150 smb_sid_split(smb_sid_t *sid, uint32_t *rid)
151 {
152 smb_sid_t *domsid;
153
154 if (!smb_sid_isvalid(sid) || (sid->sid_subauthcnt == 0))
155 return (NULL);
156
157 if ((domsid = smb_sid_dup(sid)) == NULL)
158 return (NULL);
159
160 --domsid->sid_subauthcnt;
161 if (rid)
162 *rid = domsid->sid_subauth[domsid->sid_subauthcnt];
163
164 return (domsid);
165 }
166
167 /*
168 * smb_sid_splitstr
169 *
170 * Takes a full sid in string form and split it into a domain sid and a
171 * relative id (rid).
172 *
173 * IMPORTANT: The original sid is modified in place. This function assumes
174 * given SID is in valid string format.
175 */
176 int
smb_sid_splitstr(char * strsid,uint32_t * rid)177 smb_sid_splitstr(char *strsid, uint32_t *rid)
178 {
179 char *p;
180
181 if ((p = strrchr(strsid, '-')) == NULL)
182 return (-1);
183
184 *p++ = '\0';
185 if (rid) {
186 #ifdef _KERNEL
187 unsigned long sua = 0;
188 (void) ddi_strtoul(p, NULL, 10, &sua);
189 *rid = (uint32_t)sua;
190 #else
191 *rid = strtoul(p, NULL, 10);
192 #endif
193 }
194
195 return (0);
196 }
197
198 /*
199 * smb_sid_cmp
200 *
201 * Compare two SIDs and return a boolean result. The checks are ordered
202 * such that components that are more likely to differ are checked
203 * first. For example, after checking that the SIDs contain the same
204 * sid_subauthcnt, we check the sub-authorities in reverse order because
205 * the RID is the most likely differentiator between two SIDs, i.e.
206 * they are probably going to be in the same domain.
207 */
208 boolean_t
smb_sid_cmp(smb_sid_t * sid1,smb_sid_t * sid2)209 smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2)
210 {
211 int i;
212
213 if (sid1 == NULL || sid2 == NULL)
214 return (B_FALSE);
215
216 if (sid1->sid_subauthcnt != sid2->sid_subauthcnt ||
217 sid1->sid_revision != sid2->sid_revision)
218 return (B_FALSE);
219
220 for (i = sid1->sid_subauthcnt - 1; i >= 0; --i)
221 if (sid1->sid_subauth[i] != sid2->sid_subauth[i])
222 return (B_FALSE);
223
224 if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX))
225 return (B_FALSE);
226
227 return (B_TRUE);
228 }
229
230 /*
231 * smb_sid_indomain
232 *
233 * Check if given SID is in given domain.
234 */
235 boolean_t
smb_sid_indomain(smb_sid_t * domain_sid,smb_sid_t * sid)236 smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid)
237 {
238 int i;
239
240 if (sid == NULL || domain_sid == NULL)
241 return (B_FALSE);
242
243 if (domain_sid->sid_revision != sid->sid_revision ||
244 sid->sid_subauthcnt < domain_sid->sid_subauthcnt)
245 return (B_FALSE);
246
247 for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i)
248 if (domain_sid->sid_subauth[i] != sid->sid_subauth[i])
249 return (B_FALSE);
250
251 if (bcmp(&domain_sid->sid_authority, &sid->sid_authority,
252 NT_SID_AUTH_MAX))
253 return (B_FALSE);
254
255 return (B_TRUE);
256 }
257
258 #ifndef _KERNEL
259 /*
260 * smb_sid_islocal
261 *
262 * Check a SID to see if it belongs to the local domain.
263 */
264 boolean_t
smb_sid_islocal(smb_sid_t * sid)265 smb_sid_islocal(smb_sid_t *sid)
266 {
267 smb_domain_t di;
268 boolean_t islocal = B_FALSE;
269
270 if (smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
271 islocal = smb_sid_indomain(di.di_binsid, sid);
272
273 return (islocal);
274 }
275 #endif /* _KERNEL */
276
277 /*
278 * smb_sid_tostr
279 *
280 * Fill in the passed buffer with the string form of the given
281 * binary sid.
282 */
283 void
smb_sid_tostr(const smb_sid_t * sid,char * strsid)284 smb_sid_tostr(const smb_sid_t *sid, char *strsid)
285 {
286 char *p = strsid;
287 int i;
288
289 if (sid == NULL || strsid == NULL)
290 return;
291
292 (void) sprintf(p, "S-%d-", sid->sid_revision);
293 while (*p)
294 p++;
295
296 for (i = 0; i < NT_SID_AUTH_MAX; ++i) {
297 if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) {
298 (void) sprintf(p, "%d", sid->sid_authority[i]);
299 while (*p)
300 p++;
301 }
302 }
303
304 for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) {
305 (void) sprintf(p, "-%u", sid->sid_subauth[i]);
306 while (*p)
307 p++;
308 }
309 }
310
311 /*
312 * smb_sid_fromstr
313 *
314 * Converts a SID in string form to a SID structure. There are lots of
315 * simplifying assumptions in here. The memory for the SID is allocated
316 * as if it was the largest possible SID; the caller is responsible for
317 * freeing the memory when it is no longer required. We assume that the
318 * string starts with "S-1-" and that the authority is held in the last
319 * byte, which should be okay for most situations. It also assumes the
320 * sub-authorities are in decimal format.
321 *
322 * On success, a pointer to a SID is returned. Otherwise a null pointer
323 * is returned.
324 */
325 #ifdef _KERNEL
326 smb_sid_t *
smb_sid_fromstr(const char * sidstr)327 smb_sid_fromstr(const char *sidstr)
328 {
329 smb_sid_t *sid;
330 smb_sid_t *retsid;
331 const char *p;
332 int size;
333 uint8_t i;
334 unsigned long sua;
335
336 if (sidstr == NULL)
337 return (NULL);
338
339 if (strncmp(sidstr, "S-1-", 4) != 0)
340 return (NULL);
341
342 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
343 sid = kmem_zalloc(size, KM_SLEEP);
344
345 sid->sid_revision = NT_SID_REVISION;
346 sua = 0;
347 (void) ddi_strtoul(&sidstr[4], 0, 10, &sua);
348 sid->sid_authority[5] = (uint8_t)sua;
349
350 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
351 while (*p && *p == '-')
352 ++p;
353
354 if (*p < '0' || *p > '9') {
355 kmem_free(sid, size);
356 return (NULL);
357 }
358
359 sua = 0;
360 (void) ddi_strtoul(p, 0, 10, &sua);
361 sid->sid_subauth[i] = (uint32_t)sua;
362
363 while (*p && *p != '-')
364 ++p;
365 }
366
367 sid->sid_subauthcnt = i;
368 retsid = smb_sid_dup(sid);
369 kmem_free(sid, size);
370
371 return (retsid);
372 }
373 #else /* _KERNEL */
374 smb_sid_t *
smb_sid_fromstr(const char * sidstr)375 smb_sid_fromstr(const char *sidstr)
376 {
377 smb_sid_t *sid;
378 const char *p;
379 int size;
380 uint8_t i;
381
382 if (sidstr == NULL)
383 return (NULL);
384
385 if (strncmp(sidstr, "S-1-", 4) != 0)
386 return (NULL);
387
388 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
389
390 if ((sid = malloc(size)) == NULL)
391 return (NULL);
392
393 bzero(sid, size);
394 sid->sid_revision = NT_SID_REVISION;
395 sid->sid_authority[5] = atoi(&sidstr[4]);
396
397 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
398 while (*p && *p == '-')
399 ++p;
400
401 if (*p < '0' || *p > '9') {
402 free(sid);
403 return (NULL);
404 }
405
406 sid->sid_subauth[i] = strtoul(p, NULL, 10);
407
408 while (*p && *p != '-')
409 ++p;
410 }
411
412 sid->sid_subauthcnt = i;
413 return (sid);
414 }
415 #endif /* _KERNEL */
416
417 /*
418 * smb_sid_type2str
419 *
420 * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE
421 * provides the context for a SID, i.e. the type of resource to which
422 * it refers.
423 */
424 char *
smb_sid_type2str(uint16_t snu_id)425 smb_sid_type2str(uint16_t snu_id)
426 {
427 static char *snu_name[] = {
428 "SidTypeSidPrefix",
429 "SidTypeUser",
430 "SidTypeGroup",
431 "SidTypeDomain",
432 "SidTypeAlias",
433 "SidTypeWellKnownGroup",
434 "SidTypeDeletedAccount",
435 "SidTypeInvalid",
436 "SidTypeUnknown",
437 "SidTypeComputer",
438 "SidTypeLabel"
439 };
440
441 if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0]))))
442 return (snu_name[snu_id]);
443
444 return (snu_name[SidTypeUnknown]);
445 }
446
447 static smb_sid_t *
smb_sid_alloc(size_t size)448 smb_sid_alloc(size_t size)
449 {
450 smb_sid_t *sid;
451 #ifdef _KERNEL
452 sid = kmem_alloc(size, KM_SLEEP);
453 #else
454 sid = malloc(size);
455 #endif
456 return (sid);
457 }
458
459 void
smb_sid_free(smb_sid_t * sid)460 smb_sid_free(smb_sid_t *sid)
461 {
462 #ifdef _KERNEL
463 if (sid == NULL)
464 return;
465
466 kmem_free(sid, smb_sid_len(sid));
467 #else
468 free(sid);
469 #endif
470 }
471
472 #ifndef _KERNEL
473 void
smb_ids_free(smb_ids_t * ids)474 smb_ids_free(smb_ids_t *ids)
475 {
476 smb_id_t *id;
477 int i;
478
479 if ((ids != NULL) && (ids->i_ids != NULL)) {
480 id = ids->i_ids;
481 for (i = 0; i < ids->i_cnt; i++, id++)
482 smb_sid_free(id->i_sid);
483
484 free(ids->i_ids);
485 }
486 }
487 #endif
488