1/*
2   Unix SMB/CIFS implementation.
3   Samba utility functions
4
5   Copyright (C) Stefan (metze) Metzmacher 	2002-2004
6   Copyright (C) Andrew Tridgell 		1992-2004
7   Copyright (C) Jeremy Allison  		1999
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; either version 3 of the License, or
12   (at your option) any later version.
13
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18
19   You should have received a copy of the GNU General Public License
20   along with this program.  If not, see <http://www.gnu.org/licenses/>.
21*/
22
23#include "includes.h"
24#include "librpc/gen_ndr/security.h"
25#include "dom_sid.h"
26
27/*****************************************************************
28 Compare the auth portion of two sids.
29*****************************************************************/
30
31int dom_sid_compare_auth(const struct dom_sid *sid1,
32			 const struct dom_sid *sid2)
33{
34	int i;
35
36	if (sid1 == sid2)
37		return 0;
38	if (!sid1)
39		return -1;
40	if (!sid2)
41		return 1;
42
43	if (sid1->sid_rev_num != sid2->sid_rev_num)
44		return sid1->sid_rev_num - sid2->sid_rev_num;
45
46	for (i = 0; i < 6; i++)
47		if (sid1->id_auth[i] != sid2->id_auth[i])
48			return sid1->id_auth[i] - sid2->id_auth[i];
49
50	return 0;
51}
52
53/*****************************************************************
54 Compare two sids.
55*****************************************************************/
56
57int dom_sid_compare(const struct dom_sid *sid1, const struct dom_sid *sid2)
58{
59	int i;
60
61	if (sid1 == sid2)
62		return 0;
63	if (!sid1)
64		return -1;
65	if (!sid2)
66		return 1;
67
68	/* Compare most likely different rids, first: i.e start at end */
69	if (sid1->num_auths != sid2->num_auths)
70		return sid1->num_auths - sid2->num_auths;
71
72	for (i = sid1->num_auths-1; i >= 0; --i)
73		if (sid1->sub_auths[i] != sid2->sub_auths[i])
74			return sid1->sub_auths[i] - sid2->sub_auths[i];
75
76	return dom_sid_compare_auth(sid1, sid2);
77}
78
79/*****************************************************************
80 Compare two sids.
81*****************************************************************/
82
83bool dom_sid_equal(const struct dom_sid *sid1, const struct dom_sid *sid2)
84{
85	return dom_sid_compare(sid1, sid2) == 0;
86}
87
88/*****************************************************************
89 Add a rid to the end of a sid
90*****************************************************************/
91
92bool sid_append_rid(struct dom_sid *sid, uint32_t rid)
93{
94	if (sid->num_auths < ARRAY_SIZE(sid->sub_auths)) {
95		sid->sub_auths[sid->num_auths++] = rid;
96		return true;
97	}
98	return false;
99}
100
101/*
102  See if 2 SIDs are in the same domain
103  this just compares the leading sub-auths
104*/
105int dom_sid_compare_domain(const struct dom_sid *sid1,
106			   const struct dom_sid *sid2)
107{
108	int n, i;
109
110	n = MIN(sid1->num_auths, sid2->num_auths);
111
112	for (i = n-1; i >= 0; --i)
113		if (sid1->sub_auths[i] != sid2->sub_auths[i])
114			return sid1->sub_auths[i] - sid2->sub_auths[i];
115
116	return dom_sid_compare_auth(sid1, sid2);
117}
118
119/*****************************************************************
120 Convert a string to a SID. Returns True on success, False on fail.
121 Return the first character not parsed in endp.
122*****************************************************************/
123#define AUTHORITY_MASK (~(0xffffffffffffULL))
124
125bool dom_sid_parse_endp(const char *sidstr,struct dom_sid *sidout,
126			const char **endp)
127{
128	const char *p;
129	char *q;
130	/* BIG NOTE: this function only does SIDS where the identauth is not >= 2^32 */
131	uint64_t conv;
132
133	ZERO_STRUCTP(sidout);
134
135	if ((sidstr[0] != 'S' && sidstr[0] != 's') || sidstr[1] != '-') {
136		goto format_error;
137	}
138
139	/* Get the revision number. */
140	p = sidstr + 2;
141
142	if (!isdigit(*p)) {
143		goto format_error;
144	}
145
146	conv = strtoul(p, &q, 10);
147	if (!q || (*q != '-') || conv > UINT8_MAX) {
148		goto format_error;
149	}
150	sidout->sid_rev_num = (uint8_t) conv;
151	q++;
152
153	if (!isdigit(*q)) {
154		goto format_error;
155	}
156
157	/* get identauth */
158	conv = strtoull(q, &q, 0);
159	if (!q || conv & AUTHORITY_MASK) {
160		goto format_error;
161	}
162
163	/* When identauth >= UINT32_MAX, it's in hex with a leading 0x */
164	/* NOTE - the conv value is in big-endian format. */
165	sidout->id_auth[0] = (conv & 0xff0000000000ULL) >> 40;
166	sidout->id_auth[1] = (conv & 0x00ff00000000ULL) >> 32;
167	sidout->id_auth[2] = (conv & 0x0000ff000000ULL) >> 24;
168	sidout->id_auth[3] = (conv & 0x000000ff0000ULL) >> 16;
169	sidout->id_auth[4] = (conv & 0x00000000ff00ULL) >> 8;
170	sidout->id_auth[5] = (conv & 0x0000000000ffULL);
171
172	sidout->num_auths = 0;
173	if (*q != '-') {
174		/* Just id_auth, no subauths */
175		return true;
176	}
177
178	q++;
179
180	while (true) {
181		char *end;
182
183		if (!isdigit(*q)) {
184			goto format_error;
185		}
186
187		conv = strtoull(q, &end, 10);
188		if (end == q || conv > UINT32_MAX) {
189			goto format_error;
190		}
191
192		if (!sid_append_rid(sidout, conv)) {
193			DEBUG(3, ("Too many sid auths in %s\n", sidstr));
194			return false;
195		}
196
197		q = end;
198		if (*q != '-') {
199			break;
200		}
201		q += 1;
202	}
203	if (endp != NULL) {
204		*endp = q;
205	}
206	return true;
207
208format_error:
209	DEBUG(3, ("string_to_sid: SID %s is not in a valid format\n", sidstr));
210	return false;
211}
212
213bool string_to_sid(struct dom_sid *sidout, const char *sidstr)
214{
215	return dom_sid_parse(sidstr, sidout);
216}
217
218bool dom_sid_parse(const char *sidstr, struct dom_sid *ret)
219{
220	return dom_sid_parse_endp(sidstr, ret, NULL);
221}
222
223/*
224  convert a string to a dom_sid, returning a talloc'd dom_sid
225*/
226struct dom_sid *dom_sid_parse_talloc(TALLOC_CTX *mem_ctx, const char *sidstr)
227{
228	struct dom_sid *ret;
229	ret = talloc(mem_ctx, struct dom_sid);
230	if (!ret) {
231		return NULL;
232	}
233	if (!dom_sid_parse(sidstr, ret)) {
234		talloc_free(ret);
235		return NULL;
236	}
237
238	return ret;
239}
240
241/*
242  convert a string to a dom_sid, returning a talloc'd dom_sid
243*/
244struct dom_sid *dom_sid_parse_length(TALLOC_CTX *mem_ctx, const DATA_BLOB *sid)
245{
246	char p[sid->length+1];
247	memcpy(p, sid->data, sid->length);
248	p[sid->length] = '\0';
249	return dom_sid_parse_talloc(mem_ctx, p);
250}
251
252/*
253  copy a dom_sid structure
254*/
255struct dom_sid *dom_sid_dup(TALLOC_CTX *mem_ctx, const struct dom_sid *dom_sid)
256{
257	struct dom_sid *ret;
258	int i;
259
260	if (!dom_sid) {
261		return NULL;
262	}
263
264	ret = talloc(mem_ctx, struct dom_sid);
265	if (!ret) {
266		return NULL;
267	}
268
269	ret->sid_rev_num = dom_sid->sid_rev_num;
270	ret->id_auth[0] = dom_sid->id_auth[0];
271	ret->id_auth[1] = dom_sid->id_auth[1];
272	ret->id_auth[2] = dom_sid->id_auth[2];
273	ret->id_auth[3] = dom_sid->id_auth[3];
274	ret->id_auth[4] = dom_sid->id_auth[4];
275	ret->id_auth[5] = dom_sid->id_auth[5];
276	ret->num_auths = dom_sid->num_auths;
277
278	for (i=0;i<dom_sid->num_auths;i++) {
279		ret->sub_auths[i] = dom_sid->sub_auths[i];
280	}
281
282	return ret;
283}
284
285/*
286  add a rid to a domain dom_sid to make a full dom_sid. This function
287  returns a new sid in the supplied memory context
288*/
289struct dom_sid *dom_sid_add_rid(TALLOC_CTX *mem_ctx,
290				const struct dom_sid *domain_sid,
291				uint32_t rid)
292{
293	struct dom_sid *sid;
294
295	sid = dom_sid_dup(mem_ctx, domain_sid);
296	if (!sid) return NULL;
297
298	if (!sid_append_rid(sid, rid)) {
299		talloc_free(sid);
300		return NULL;
301	}
302
303	return sid;
304}
305
306/*
307  Split up a SID into its domain and RID part
308*/
309NTSTATUS dom_sid_split_rid(TALLOC_CTX *mem_ctx, const struct dom_sid *sid,
310			   struct dom_sid **domain, uint32_t *rid)
311{
312	if (sid->num_auths == 0) {
313		return NT_STATUS_INVALID_PARAMETER;
314	}
315
316	if (domain) {
317		if (!(*domain = dom_sid_dup(mem_ctx, sid))) {
318			return NT_STATUS_NO_MEMORY;
319		}
320
321		(*domain)->num_auths -= 1;
322	}
323
324	if (rid) {
325		*rid = sid->sub_auths[sid->num_auths - 1];
326	}
327
328	return NT_STATUS_OK;
329}
330
331/*
332  return true if the 2nd sid is in the domain given by the first sid
333*/
334bool dom_sid_in_domain(const struct dom_sid *domain_sid,
335		       const struct dom_sid *sid)
336{
337	int i;
338
339	if (!domain_sid || !sid) {
340		return false;
341	}
342
343	if (domain_sid->num_auths > sid->num_auths) {
344		return false;
345	}
346
347	for (i = domain_sid->num_auths-1; i >= 0; --i) {
348		if (domain_sid->sub_auths[i] != sid->sub_auths[i]) {
349			return false;
350		}
351	}
352
353	return dom_sid_compare_auth(domain_sid, sid) == 0;
354}
355
356/*
357  Convert a dom_sid to a string, printing into a buffer. Return the
358  string length. If it overflows, return the string length that would
359  result (buflen needs to be +1 for the terminating 0).
360*/
361int dom_sid_string_buf(const struct dom_sid *sid, char *buf, int buflen)
362{
363	int i, ofs;
364	uint64_t ia;
365
366	if (!sid) {
367		return strlcpy(buf, "(NULL SID)", buflen);
368	}
369
370	ia = ((uint64_t)sid->id_auth[5]) +
371		((uint64_t)sid->id_auth[4] << 8 ) +
372		((uint64_t)sid->id_auth[3] << 16) +
373		((uint64_t)sid->id_auth[2] << 24) +
374		((uint64_t)sid->id_auth[1] << 32) +
375		((uint64_t)sid->id_auth[0] << 40);
376
377	ofs = snprintf(buf, buflen, "S-%hhu-", (unsigned char)sid->sid_rev_num);
378	if (ia >= UINT32_MAX) {
379		ofs += snprintf(buf + ofs, MAX(buflen - ofs, 0), "0x%llx",
380				(unsigned long long)ia);
381	} else {
382		ofs += snprintf(buf + ofs, MAX(buflen - ofs, 0), "%llu",
383				(unsigned long long)ia);
384	}
385
386	for (i = 0; i < sid->num_auths; i++) {
387		ofs += snprintf(buf + ofs, MAX(buflen - ofs, 0), "-%u",
388				(unsigned int)sid->sub_auths[i]);
389	}
390	return ofs;
391}
392
393/*
394  convert a dom_sid to a string
395*/
396char *dom_sid_string(TALLOC_CTX *mem_ctx, const struct dom_sid *sid)
397{
398	char buf[DOM_SID_STR_BUFLEN];
399	char *result;
400	int len;
401
402	len = dom_sid_string_buf(sid, buf, sizeof(buf));
403
404	if (len+1 > sizeof(buf)) {
405		return talloc_strdup(mem_ctx, "(SID ERR)");
406	}
407
408	/*
409	 * Avoid calling strlen (via talloc_strdup), we already have
410	 * the length
411	 */
412	result = (char *)talloc_memdup(mem_ctx, buf, len+1);
413	if (result == NULL) {
414		return NULL;
415	}
416
417	/*
418	 * beautify the talloc_report output
419	 */
420	talloc_set_name_const(result, result);
421	return result;
422}
423