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 /* We will reduce sid_subauthcnt by one. */
160 size = smb_sid_len(sid) - sizeof (uint32_t);
161 if ((domsid = smb_sid_alloc(size)) == NULL)
162 return (NULL);
163
164 bcopy(sid, domsid, size);
165 domsid->sid_subauthcnt = sid->sid_subauthcnt - 1;
166
167 if (rid)
168 *rid = domsid->sid_subauth[domsid->sid_subauthcnt];
169
170 return (domsid);
171 }
172
173 /*
174 * smb_sid_splitstr
175 *
176 * Takes a full sid in string form and split it into a domain sid and a
177 * relative id (rid).
178 *
179 * IMPORTANT: The original sid is modified in place. This function assumes
180 * given SID is in valid string format.
181 */
182 int
smb_sid_splitstr(char * strsid,uint32_t * rid)183 smb_sid_splitstr(char *strsid, uint32_t *rid)
184 {
185 char *p;
186
187 if ((p = strrchr(strsid, '-')) == NULL)
188 return (-1);
189
190 *p++ = '\0';
191 if (rid) {
192 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
193 unsigned long sua = 0;
194 (void) ddi_strtoul(p, NULL, 10, &sua);
195 *rid = (uint32_t)sua;
196 #else
197 *rid = strtoul(p, NULL, 10);
198 #endif
199 }
200
201 return (0);
202 }
203
204 /*
205 * smb_sid_cmp
206 *
207 * Compare two SIDs and return a boolean result. The checks are ordered
208 * such that components that are more likely to differ are checked
209 * first. For example, after checking that the SIDs contain the same
210 * sid_subauthcnt, we check the sub-authorities in reverse order because
211 * the RID is the most likely differentiator between two SIDs, i.e.
212 * they are probably going to be in the same domain.
213 */
214 boolean_t
smb_sid_cmp(smb_sid_t * sid1,smb_sid_t * sid2)215 smb_sid_cmp(smb_sid_t *sid1, smb_sid_t *sid2)
216 {
217 int i;
218
219 if (sid1 == NULL || sid2 == NULL)
220 return (B_FALSE);
221
222 if (sid1->sid_subauthcnt != sid2->sid_subauthcnt ||
223 sid1->sid_revision != sid2->sid_revision)
224 return (B_FALSE);
225
226 for (i = sid1->sid_subauthcnt - 1; i >= 0; --i)
227 if (sid1->sid_subauth[i] != sid2->sid_subauth[i])
228 return (B_FALSE);
229
230 if (bcmp(&sid1->sid_authority, &sid2->sid_authority, NT_SID_AUTH_MAX))
231 return (B_FALSE);
232
233 return (B_TRUE);
234 }
235
236 /*
237 * smb_sid_indomain
238 *
239 * Check if given SID is in given domain.
240 */
241 boolean_t
smb_sid_indomain(smb_sid_t * domain_sid,smb_sid_t * sid)242 smb_sid_indomain(smb_sid_t *domain_sid, smb_sid_t *sid)
243 {
244 int i;
245
246 if (sid == NULL || domain_sid == NULL)
247 return (B_FALSE);
248
249 if (domain_sid->sid_revision != sid->sid_revision ||
250 sid->sid_subauthcnt < domain_sid->sid_subauthcnt)
251 return (B_FALSE);
252
253 for (i = domain_sid->sid_subauthcnt - 1; i >= 0; --i)
254 if (domain_sid->sid_subauth[i] != sid->sid_subauth[i])
255 return (B_FALSE);
256
257 if (bcmp(&domain_sid->sid_authority, &sid->sid_authority,
258 NT_SID_AUTH_MAX))
259 return (B_FALSE);
260
261 return (B_TRUE);
262 }
263
264 /*
265 * smb_sid_tostr
266 *
267 * Fill in the passed buffer with the string form of the given
268 * binary sid.
269 */
270 void
smb_sid_tostr(const smb_sid_t * sid,char * strsid)271 smb_sid_tostr(const smb_sid_t *sid, char *strsid)
272 {
273 char *p = strsid;
274 int i;
275
276 if (sid == NULL || strsid == NULL)
277 return;
278
279 (void) sprintf(p, "S-%d-", sid->sid_revision);
280 while (*p)
281 p++;
282
283 for (i = 0; i < NT_SID_AUTH_MAX; ++i) {
284 if (sid->sid_authority[i] != 0 || i == NT_SID_AUTH_MAX - 1) {
285 (void) sprintf(p, "%d", sid->sid_authority[i]);
286 while (*p)
287 p++;
288 }
289 }
290
291 for (i = 0; i < sid->sid_subauthcnt && i < NT_SID_SUBAUTH_MAX; ++i) {
292 (void) sprintf(p, "-%u", sid->sid_subauth[i]);
293 while (*p)
294 p++;
295 }
296 }
297
298 /*
299 * smb_sid_fromstr
300 *
301 * Converts a SID in string form to a SID structure. There are lots of
302 * simplifying assumptions in here. The memory for the SID is allocated
303 * as if it was the largest possible SID; the caller is responsible for
304 * freeing the memory when it is no longer required. We assume that the
305 * string starts with "S-1-" and that the authority is held in the last
306 * byte, which should be okay for most situations. It also assumes the
307 * sub-authorities are in decimal format.
308 *
309 * On success, a pointer to a SID is returned. Otherwise a null pointer
310 * is returned.
311 */
312 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
313 smb_sid_t *
smb_sid_fromstr(const char * sidstr)314 smb_sid_fromstr(const char *sidstr)
315 {
316 smb_sid_t *sid;
317 smb_sid_t *retsid;
318 const char *p;
319 int size;
320 uint8_t i;
321 unsigned long sua;
322
323 if (sidstr == NULL)
324 return (NULL);
325
326 if (strncmp(sidstr, "S-1-", 4) != 0)
327 return (NULL);
328
329 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
330 sid = kmem_zalloc(size, KM_SLEEP);
331
332 sid->sid_revision = NT_SID_REVISION;
333 sua = 0;
334 (void) ddi_strtoul(&sidstr[4], 0, 10, &sua);
335 sid->sid_authority[5] = (uint8_t)sua;
336
337 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
338 while (*p && *p == '-')
339 ++p;
340
341 if (*p < '0' || *p > '9') {
342 kmem_free(sid, size);
343 return (NULL);
344 }
345
346 sua = 0;
347 (void) ddi_strtoul(p, 0, 10, &sua);
348 sid->sid_subauth[i] = (uint32_t)sua;
349
350 while (*p && *p != '-')
351 ++p;
352 }
353
354 sid->sid_subauthcnt = i;
355 retsid = smb_sid_dup(sid);
356 kmem_free(sid, size);
357
358 return (retsid);
359 }
360 #else /* _KERNEL */
361 smb_sid_t *
smb_sid_fromstr(const char * sidstr)362 smb_sid_fromstr(const char *sidstr)
363 {
364 smb_sid_t *sid;
365 const char *p;
366 int size;
367 uint8_t i;
368
369 if (sidstr == NULL)
370 return (NULL);
371
372 if (strncmp(sidstr, "S-1-", 4) != 0)
373 return (NULL);
374
375 size = sizeof (smb_sid_t) + (NT_SID_SUBAUTH_MAX * sizeof (uint32_t));
376
377 if ((sid = malloc(size)) == NULL)
378 return (NULL);
379
380 bzero(sid, size);
381 sid->sid_revision = NT_SID_REVISION;
382 sid->sid_authority[5] = atoi(&sidstr[4]);
383
384 for (i = 0, p = &sidstr[5]; i < NT_SID_SUBAUTH_MAX && *p; ++i) {
385 while (*p && *p == '-')
386 ++p;
387
388 if (*p < '0' || *p > '9') {
389 free(sid);
390 return (NULL);
391 }
392
393 sid->sid_subauth[i] = strtoul(p, NULL, 10);
394
395 while (*p && *p != '-')
396 ++p;
397 }
398
399 sid->sid_subauthcnt = i;
400 return (sid);
401 }
402 #endif /* _KERNEL */
403
404 /*
405 * smb_sid_type2str
406 *
407 * Returns the text name for a SID_NAME_USE value. The SID_NAME_USE
408 * provides the context for a SID, i.e. the type of resource to which
409 * it refers.
410 */
411 char *
smb_sid_type2str(uint16_t snu_id)412 smb_sid_type2str(uint16_t snu_id)
413 {
414 static char *snu_name[] = {
415 "SidTypeSidPrefix",
416 "SidTypeUser",
417 "SidTypeGroup",
418 "SidTypeDomain",
419 "SidTypeAlias",
420 "SidTypeWellKnownGroup",
421 "SidTypeDeletedAccount",
422 "SidTypeInvalid",
423 "SidTypeUnknown",
424 "SidTypeComputer",
425 "SidTypeLabel"
426 };
427
428 if (snu_id < ((sizeof (snu_name)/sizeof (snu_name[0]))))
429 return (snu_name[snu_id]);
430
431 return (snu_name[SidTypeUnknown]);
432 }
433
434 static smb_sid_t *
smb_sid_alloc(size_t size)435 smb_sid_alloc(size_t size)
436 {
437 smb_sid_t *sid;
438 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
439 sid = kmem_alloc(size, KM_SLEEP);
440 #else
441 sid = malloc(size);
442 #endif
443 return (sid);
444 }
445
446 void
smb_sid_free(smb_sid_t * sid)447 smb_sid_free(smb_sid_t *sid)
448 {
449 #if defined(_KERNEL) || defined(_FAKE_KERNEL)
450 if (sid == NULL)
451 return;
452
453 kmem_free(sid, smb_sid_len(sid));
454 #else
455 free(sid);
456 #endif
457 }
458