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