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