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