xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_idmap.c (revision e3ae4b35c024af1196582063ecee3ab79367227d)
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 2020 Nexenta by DDN, Inc. All rights reserved.
24  * Copyright 2023 RackTop Systems, Inc.
25  */
26 
27 /*
28  * SMB server interface to idmap
29  * (smb_idmap_get..., smb_idmap_batch_...)
30  *
31  * There are three implementations of this interface.
32  * This is the kernel version of these routines.  See also:
33  * $SRC/lib/smbsrv/libfksmbsrv/common/fksmb_idmap.c
34  * $SRC/lib/smbsrv/libsmb/common/smb_idmap.c
35  *
36  * There are enough differences (relative to the code size)
37  * that it's more trouble than it's worth to merge them.
38  *
39  * This one differs from the others in that it:
40  *	calls kernel (kidmap_...) interfaces
41  *	returned domain SIDs are shared, not strdup'ed
42  */
43 
44 /*
45  * SMB ID mapping
46  *
47  * Solaris ID mapping service (aka Winchester) works with domain SIDs
48  * and RIDs where domain SIDs are in string format. CIFS service works
49  * with binary SIDs understandable by CIFS clients. A layer of SMB ID
50  * mapping functions are implemeted to hide the SID conversion details
51  * and also hide the handling of array of batch mapping requests.
52  */
53 
54 #include <sys/param.h>
55 #include <sys/types.h>
56 #include <sys/tzfile.h>
57 #include <sys/atomic.h>
58 #include <sys/kidmap.h>
59 #include <sys/time.h>
60 #include <sys/spl.h>
61 #include <sys/random.h>
62 #include <smbsrv/smb_kproto.h>
63 #include <smbsrv/smb_fsops.h>
64 #include <smbsrv/smbinfo.h>
65 #include <smbsrv/smb_xdr.h>
66 #include <smbsrv/smb_vops.h>
67 #include <smbsrv/smb_idmap.h>
68 
69 #include <sys/sid.h>
70 #include <sys/priv_names.h>
71 
72 static int smb_idmap_batch_binsid(smb_idmap_batch_t *sib);
73 
74 /*
75  * smb_idmap_getsid
76  *
77  * Maps the given Solaris ID to a Windows SID using the
78  * simple mapping API.
79  */
80 idmap_stat
81 smb_idmap_getsid(uid_t id, int idtype, smb_sid_t **sid)
82 {
83 	smb_idmap_t sim;
84 
85 	switch (idtype) {
86 	case SMB_IDMAP_USER:
87 		sim.sim_stat = kidmap_getsidbyuid(curzone, id,
88 		    (const char **)&sim.sim_domsid, &sim.sim_rid);
89 		break;
90 
91 	case SMB_IDMAP_GROUP:
92 		sim.sim_stat = kidmap_getsidbygid(curzone, id,
93 		    (const char **)&sim.sim_domsid, &sim.sim_rid);
94 		break;
95 
96 	case SMB_IDMAP_EVERYONE:
97 		/* Everyone S-1-1-0 */
98 		sim.sim_domsid = "S-1-1";
99 		sim.sim_rid = 0;
100 		sim.sim_stat = IDMAP_SUCCESS;
101 		break;
102 
103 	default:
104 		ASSERT(0);
105 		return (IDMAP_ERR_ARG);
106 	}
107 
108 	/*
109 	 * IDMAP_ERR_NOTFOUND is an advisory error
110 	 * and idmap will generate a local sid.
111 	 */
112 	if (sim.sim_stat == IDMAP_ERR_NOTFOUND &&
113 	    sim.sim_domsid != NULL)
114 		sim.sim_stat = IDMAP_SUCCESS;
115 
116 	if (sim.sim_stat != IDMAP_SUCCESS)
117 		return (sim.sim_stat);
118 
119 	if (sim.sim_domsid == NULL)
120 		return (IDMAP_ERR_NOMAPPING);
121 
122 	sim.sim_sid = smb_sid_fromstr(sim.sim_domsid);
123 	if (sim.sim_sid == NULL)
124 		return (IDMAP_ERR_INTERNAL);
125 
126 	*sid = smb_sid_splice(sim.sim_sid, sim.sim_rid);
127 	smb_sid_free(sim.sim_sid);
128 	if (*sid == NULL)
129 		sim.sim_stat = IDMAP_ERR_INTERNAL;
130 
131 	return (sim.sim_stat);
132 }
133 
134 /*
135  * smb_idmap_getid
136  *
137  * Maps the given Windows SID to a Unix ID using the
138  * simple mapping API.
139  */
140 idmap_stat
141 smb_idmap_getid(smb_sid_t *sid, uid_t *id, int *idtype)
142 {
143 	smb_idmap_t sim;
144 	char sidstr[SMB_SID_STRSZ];
145 
146 	smb_sid_tostr(sid, sidstr);
147 	if (smb_sid_splitstr(sidstr, &sim.sim_rid) != 0)
148 		return (IDMAP_ERR_SID);
149 	sim.sim_domsid = sidstr;
150 	sim.sim_id = id;
151 
152 	switch (*idtype) {
153 	case SMB_IDMAP_USER:
154 		sim.sim_stat = kidmap_getuidbysid(curzone, sim.sim_domsid,
155 		    sim.sim_rid, sim.sim_id);
156 		break;
157 
158 	case SMB_IDMAP_GROUP:
159 		sim.sim_stat = kidmap_getgidbysid(curzone, sim.sim_domsid,
160 		    sim.sim_rid, sim.sim_id);
161 		break;
162 
163 	case SMB_IDMAP_UNKNOWN:
164 		sim.sim_stat = kidmap_getpidbysid(curzone, sim.sim_domsid,
165 		    sim.sim_rid, sim.sim_id, &sim.sim_idtype);
166 		break;
167 
168 	default:
169 		ASSERT(0);
170 		return (IDMAP_ERR_ARG);
171 	}
172 
173 	*idtype = sim.sim_idtype;
174 
175 	return (sim.sim_stat);
176 }
177 
178 /*
179  * smb_idmap_batch_create
180  *
181  * Creates and initializes the context for batch ID mapping.
182  */
183 idmap_stat
184 smb_idmap_batch_create(smb_idmap_batch_t *sib, uint16_t nmap, int flags)
185 {
186 	ASSERT(sib != NULL);
187 
188 	bzero(sib, sizeof (smb_idmap_batch_t));
189 
190 	sib->sib_idmaph = kidmap_get_create(curzone);
191 
192 	sib->sib_flags = flags;
193 	sib->sib_nmap = nmap;
194 	sib->sib_size = nmap * sizeof (smb_idmap_t);
195 	sib->sib_maps = kmem_zalloc(sib->sib_size, KM_SLEEP);
196 
197 	return (IDMAP_SUCCESS);
198 }
199 
200 /*
201  * smb_idmap_batch_destroy
202  *
203  * Frees the batch ID mapping context.
204  * If ID mapping is Solaris -> Windows it frees memories
205  * allocated for binary SIDs.
206  */
207 void
208 smb_idmap_batch_destroy(smb_idmap_batch_t *sib)
209 {
210 	char *domsid;
211 	int i;
212 
213 	ASSERT(sib != NULL);
214 	ASSERT(sib->sib_maps != NULL);
215 
216 	if (sib->sib_idmaph) {
217 		kidmap_get_destroy(sib->sib_idmaph);
218 		sib->sib_idmaph = NULL;
219 	}
220 
221 	if (sib->sib_flags & SMB_IDMAP_ID2SID) {
222 		/*
223 		 * SIDs are allocated only when mapping
224 		 * UID/GID to SIDs
225 		 */
226 		for (i = 0; i < sib->sib_nmap; i++)
227 			smb_sid_free(sib->sib_maps[i].sim_sid);
228 	} else if (sib->sib_flags & SMB_IDMAP_SID2ID) {
229 		/*
230 		 * SID prefixes are allocated only when mapping
231 		 * SIDs to UID/GID
232 		 */
233 		for (i = 0; i < sib->sib_nmap; i++) {
234 			domsid = sib->sib_maps[i].sim_domsid;
235 			if (domsid)
236 				smb_mem_free(domsid);
237 		}
238 	}
239 
240 	if (sib->sib_size && sib->sib_maps) {
241 		kmem_free(sib->sib_maps, sib->sib_size);
242 		sib->sib_maps = NULL;
243 	}
244 }
245 
246 /*
247  * smb_idmap_batch_getid
248  *
249  * Queue a request to map the given SID to a UID or GID.
250  *
251  * sim->sim_id should point to variable that's supposed to
252  * hold the returned UID/GID. This needs to be setup by caller
253  * of this function.
254  *
255  * If requested ID type is known, it's passed as 'idtype',
256  * if it's unknown it'll be returned in sim->sim_idtype.
257  */
258 idmap_stat
259 smb_idmap_batch_getid(idmap_get_handle_t *idmaph, smb_idmap_t *sim,
260     smb_sid_t *sid, int idtype)
261 {
262 	char strsid[SMB_SID_STRSZ];
263 	idmap_stat idm_stat;
264 
265 	ASSERT(idmaph != NULL);
266 	ASSERT(sim != NULL);
267 	ASSERT(sid != NULL);
268 
269 	smb_sid_tostr(sid, strsid);
270 	if (smb_sid_splitstr(strsid, &sim->sim_rid) != 0)
271 		return (IDMAP_ERR_SID);
272 	/* Note: Free sim_domsid in smb_idmap_batch_destroy */
273 	sim->sim_domsid = smb_mem_strdup(strsid);
274 	sim->sim_idtype = idtype;
275 
276 	switch (idtype) {
277 	case SMB_IDMAP_USER:
278 		idm_stat = kidmap_batch_getuidbysid(idmaph, sim->sim_domsid,
279 		    sim->sim_rid, sim->sim_id, &sim->sim_stat);
280 		break;
281 
282 	case SMB_IDMAP_GROUP:
283 		idm_stat = kidmap_batch_getgidbysid(idmaph, sim->sim_domsid,
284 		    sim->sim_rid, sim->sim_id, &sim->sim_stat);
285 		break;
286 
287 	case SMB_IDMAP_UNKNOWN:
288 		idm_stat = kidmap_batch_getpidbysid(idmaph, sim->sim_domsid,
289 		    sim->sim_rid, sim->sim_id, &sim->sim_idtype,
290 		    &sim->sim_stat);
291 		break;
292 
293 	default:
294 		ASSERT(0);
295 		return (IDMAP_ERR_ARG);
296 	}
297 
298 	return (idm_stat);
299 }
300 
301 /*
302  * smb_idmap_batch_getsid
303  *
304  * Queue a request to map the given UID/GID to a SID.
305  *
306  * sim->sim_domsid and sim->sim_rid will contain the mapping
307  * result upon successful process of the batched request.
308  * Stash the type for error reporting (caller saves the ID).
309  */
310 idmap_stat
311 smb_idmap_batch_getsid(idmap_get_handle_t *idmaph, smb_idmap_t *sim,
312     uid_t id, int idtype)
313 {
314 	idmap_stat idm_stat;
315 
316 	sim->sim_idtype = idtype;
317 	switch (idtype) {
318 	case SMB_IDMAP_USER:
319 		idm_stat = kidmap_batch_getsidbyuid(idmaph, id,
320 		    (const char **)&sim->sim_domsid, &sim->sim_rid,
321 		    &sim->sim_stat);
322 		break;
323 
324 	case SMB_IDMAP_GROUP:
325 		idm_stat = kidmap_batch_getsidbygid(idmaph, id,
326 		    (const char **)&sim->sim_domsid, &sim->sim_rid,
327 		    &sim->sim_stat);
328 		break;
329 
330 	case SMB_IDMAP_OWNERAT:
331 		/* Current Owner S-1-5-32-766 */
332 		sim->sim_domsid = NT_BUILTIN_DOMAIN_SIDSTR;
333 		sim->sim_rid = SECURITY_CURRENT_OWNER_RID;
334 		sim->sim_stat = IDMAP_SUCCESS;
335 		idm_stat = IDMAP_SUCCESS;
336 		break;
337 
338 	case SMB_IDMAP_GROUPAT:
339 		/* Current Group S-1-5-32-767 */
340 		sim->sim_domsid = NT_BUILTIN_DOMAIN_SIDSTR;
341 		sim->sim_rid = SECURITY_CURRENT_GROUP_RID;
342 		sim->sim_stat = IDMAP_SUCCESS;
343 		idm_stat = IDMAP_SUCCESS;
344 		break;
345 
346 	case SMB_IDMAP_EVERYONE:
347 		/* Everyone S-1-1-0 */
348 		sim->sim_domsid = NT_WORLD_AUTH_SIDSTR;
349 		sim->sim_rid = 0;
350 		sim->sim_stat = IDMAP_SUCCESS;
351 		idm_stat = IDMAP_SUCCESS;
352 		break;
353 
354 	default:
355 		ASSERT(0);
356 		return (IDMAP_ERR_ARG);
357 	}
358 
359 	return (idm_stat);
360 }
361 
362 /*
363  * smb_idmap_batch_getmappings
364  *
365  * trigger ID mapping service to get the mappings for queued
366  * requests.
367  *
368  * Checks the result of all the queued requests.
369  * If this is a Solaris -> Windows mapping it generates
370  * binary SIDs from returned (domsid, rid) pairs.
371  */
372 idmap_stat
373 smb_idmap_batch_getmappings(smb_idmap_batch_t *sib,
374     smb_idmap_batch_errcb_t errcb)
375 {
376 	idmap_stat idm_stat = IDMAP_SUCCESS;
377 	smb_idmap_t *sim;
378 	int i;
379 
380 	idm_stat = kidmap_get_mappings(sib->sib_idmaph);
381 	if (idm_stat != IDMAP_SUCCESS)
382 		return (idm_stat);
383 
384 	/*
385 	 * Check the status for all the queued requests
386 	 */
387 	for (i = 0, sim = sib->sib_maps; i < sib->sib_nmap; i++, sim++) {
388 		if (sim->sim_stat != IDMAP_SUCCESS) {
389 			sib->sib_nerr++;
390 			if (errcb != NULL)
391 				errcb(sib, sim);
392 			if ((sib->sib_flags & SMB_IDMAP_SKIP_ERRS) == 0) {
393 				return (sim->sim_stat);
394 			}
395 		}
396 	}
397 
398 	if (smb_idmap_batch_binsid(sib) != 0)
399 		idm_stat = IDMAP_ERR_OTHER;
400 
401 	return (idm_stat);
402 }
403 
404 /*
405  * smb_idmap_batch_binsid
406  *
407  * Convert sidrids to binary sids
408  *
409  * Returns 0 if successful and non-zero upon failure.
410  */
411 static int
412 smb_idmap_batch_binsid(smb_idmap_batch_t *sib)
413 {
414 	smb_sid_t *sid;
415 	smb_idmap_t *sim;
416 	int i;
417 
418 	if (sib->sib_flags & SMB_IDMAP_SID2ID)
419 		/* This operation is not required */
420 		return (0);
421 
422 	sim = sib->sib_maps;
423 	for (i = 0; i < sib->sib_nmap; sim++, i++) {
424 		ASSERT(sim->sim_domsid != NULL);
425 		if (sim->sim_domsid == NULL)
426 			return (1);
427 
428 		if ((sid = smb_sid_fromstr(sim->sim_domsid)) == NULL)
429 			return (1);
430 
431 		sim->sim_sid = smb_sid_splice(sid, sim->sim_rid);
432 		smb_sid_free(sid);
433 	}
434 
435 	return (0);
436 }
437