xref: /illumos-gate/usr/src/lib/smbsrv/libfksmbsrv/common/fksmb_idmap.c (revision cbea7aca3fd7787405cbdbd93752998f03dfc25f)
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 2018 Nexenta Systems, 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 "fake kernel" version of these routines.  See also:
33  * $SRC/lib/smbsrv/libsmb/common/smb_idmap.c
34  * $SRC/uts/common/fs/smbsrv/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 idmap interfaces (libidmap)
41  *	uses kmem_... interfaces (libfakekernel)
42  *	uses cmn_err instead of syslog, etc.
43  * The code in this variant looks a lot like the one in libsmb.
44  */
45 
46 #include <sys/param.h>
47 #include <sys/types.h>
48 
49 #include <smbsrv/smb_kproto.h>
50 #include <smbsrv/smb_idmap.h>
51 
52 static int smb_idmap_batch_binsid(smb_idmap_batch_t *sib);
53 
54 /*
55  * Report an idmap error.
56  */
57 void
58 smb_idmap_check(const char *s, idmap_stat stat)
59 {
60 	if (stat != IDMAP_SUCCESS) {
61 		if (s == NULL)
62 			s = "smb_idmap_check";
63 
64 		cmn_err(CE_NOTE, "%s: %d", s, (int)stat);
65 	}
66 }
67 
68 /*
69  * smb_idmap_getsid
70  *
71  * Tries to get a mapping for the given uid/gid
72  * Allocates ->sim_domsid
73  */
74 idmap_stat
75 smb_idmap_getsid(uid_t id, int idtype, smb_sid_t **sid)
76 {
77 	smb_idmap_batch_t sib;
78 	idmap_stat stat;
79 
80 	stat = smb_idmap_batch_create(&sib, 1, SMB_IDMAP_ID2SID);
81 	if (stat != IDMAP_SUCCESS)
82 		return (stat);
83 
84 	stat = smb_idmap_batch_getsid(sib.sib_idmaph, &sib.sib_maps[0],
85 	    id, idtype);
86 
87 	if (stat != IDMAP_SUCCESS) {
88 		smb_idmap_batch_destroy(&sib);
89 		return (stat);
90 	}
91 
92 	/* Leave error reporting to the caller. */
93 	stat = smb_idmap_batch_getmappings(&sib, NULL);
94 
95 	if (stat != IDMAP_SUCCESS) {
96 		smb_idmap_batch_destroy(&sib);
97 		return (stat);
98 	}
99 
100 	*sid = smb_sid_dup(sib.sib_maps[0].sim_sid);
101 
102 	smb_idmap_batch_destroy(&sib);
103 
104 	return (IDMAP_SUCCESS);
105 }
106 
107 /*
108  * smb_idmap_getid
109  *
110  * Tries to get a mapping for the given SID
111  */
112 idmap_stat
113 smb_idmap_getid(smb_sid_t *sid, uid_t *id, int *id_type)
114 {
115 	smb_idmap_batch_t sib;
116 	smb_idmap_t *sim;
117 	idmap_stat stat;
118 
119 	stat = smb_idmap_batch_create(&sib, 1, SMB_IDMAP_SID2ID);
120 	if (stat != IDMAP_SUCCESS)
121 		return (stat);
122 
123 	sim = &sib.sib_maps[0];
124 	sim->sim_id = id;
125 	stat = smb_idmap_batch_getid(sib.sib_idmaph, sim, sid, *id_type);
126 	if (stat != IDMAP_SUCCESS) {
127 		smb_idmap_batch_destroy(&sib);
128 		return (stat);
129 	}
130 
131 	/* Leave error reporting to the caller. */
132 	stat = smb_idmap_batch_getmappings(&sib, NULL);
133 
134 	if (stat != IDMAP_SUCCESS) {
135 		smb_idmap_batch_destroy(&sib);
136 		return (stat);
137 	}
138 
139 	*id_type = sim->sim_idtype;
140 	smb_idmap_batch_destroy(&sib);
141 
142 	return (IDMAP_SUCCESS);
143 }
144 
145 /*
146  * smb_idmap_batch_create
147  *
148  * Creates and initializes the context for batch ID mapping.
149  */
150 idmap_stat
151 smb_idmap_batch_create(smb_idmap_batch_t *sib, uint16_t nmap, int flags)
152 {
153 	idmap_stat	stat;
154 
155 	ASSERT(sib != NULL);
156 
157 	bzero(sib, sizeof (smb_idmap_batch_t));
158 	stat = idmap_get_create(&sib->sib_idmaph);
159 
160 	if (stat != IDMAP_SUCCESS) {
161 		smb_idmap_check("idmap_get_create", stat);
162 		return (stat);
163 	}
164 
165 	sib->sib_flags = flags;
166 	sib->sib_nmap = nmap;
167 	sib->sib_size = nmap * sizeof (smb_idmap_t);
168 	sib->sib_maps = kmem_zalloc(sib->sib_size, KM_SLEEP);
169 
170 	return (IDMAP_SUCCESS);
171 }
172 
173 /*
174  * smb_idmap_batch_destroy
175  *
176  * Frees the batch ID mapping context.
177  */
178 void
179 smb_idmap_batch_destroy(smb_idmap_batch_t *sib)
180 {
181 	char *domsid;
182 	int i;
183 
184 	ASSERT(sib != NULL);
185 	ASSERT(sib->sib_maps != NULL);
186 
187 	if (sib->sib_idmaph) {
188 		idmap_get_destroy(sib->sib_idmaph);
189 		sib->sib_idmaph = NULL;
190 	}
191 
192 	if (sib->sib_flags & SMB_IDMAP_ID2SID) {
193 		/*
194 		 * SIDs are allocated only when mapping
195 		 * UID/GID to SIDs
196 		 */
197 		for (i = 0; i < sib->sib_nmap; i++) {
198 			smb_sid_free(sib->sib_maps[i].sim_sid);
199 			/* from strdup() in libidmap */
200 			free(sib->sib_maps[i].sim_domsid);
201 		}
202 	} else if (sib->sib_flags & SMB_IDMAP_SID2ID) {
203 		/*
204 		 * SID prefixes are allocated only when mapping
205 		 * SIDs to UID/GID
206 		 */
207 		for (i = 0; i < sib->sib_nmap; i++) {
208 			domsid = sib->sib_maps[i].sim_domsid;
209 			if (domsid)
210 				smb_mem_free(domsid);
211 		}
212 	}
213 
214 	if (sib->sib_size && sib->sib_maps) {
215 		kmem_free(sib->sib_maps, sib->sib_size);
216 		sib->sib_maps = NULL;
217 	}
218 }
219 
220 /*
221  * smb_idmap_batch_getid
222  *
223  * Queue a request to map the given SID to a UID or GID.
224  *
225  * sim->sim_id should point to variable that's supposed to
226  * hold the returned UID/GID. This needs to be setup by caller
227  * of this function.
228  * If requested ID type is known, it's passed as 'idtype',
229  * if it's unknown it'll be returned in sim->sim_idtype.
230  */
231 idmap_stat
232 smb_idmap_batch_getid(idmap_get_handle_t *idmaph, smb_idmap_t *sim,
233     smb_sid_t *sid, int idtype)
234 {
235 	char sidstr[SMB_SID_STRSZ];
236 	idmap_stat stat;
237 	int flag = 0;
238 
239 	ASSERT(idmaph != NULL);
240 	ASSERT(sim != NULL);
241 	ASSERT(sid != NULL);
242 
243 	smb_sid_tostr(sid, sidstr);
244 	if (smb_sid_splitstr(sidstr, &sim->sim_rid) != 0)
245 		return (IDMAP_ERR_SID);
246 	/* Note: Free sim_domsid in smb_idmap_batch_destroy */
247 	sim->sim_domsid = smb_mem_strdup(sidstr);
248 	sim->sim_idtype = idtype;
249 
250 	switch (idtype) {
251 	case SMB_IDMAP_USER:
252 		stat = idmap_get_uidbysid(idmaph, sim->sim_domsid,
253 		    sim->sim_rid, flag, sim->sim_id, &sim->sim_stat);
254 		smb_idmap_check("idmap_get_uidbysid", stat);
255 		break;
256 
257 	case SMB_IDMAP_GROUP:
258 		stat = idmap_get_gidbysid(idmaph, sim->sim_domsid,
259 		    sim->sim_rid, flag, sim->sim_id, &sim->sim_stat);
260 		smb_idmap_check("idmap_get_gidbysid", stat);
261 		break;
262 
263 	case SMB_IDMAP_UNKNOWN:
264 		stat = idmap_get_pidbysid(idmaph, sim->sim_domsid,
265 		    sim->sim_rid, flag, sim->sim_id, &sim->sim_idtype,
266 		    &sim->sim_stat);
267 		smb_idmap_check("idmap_get_pidbysid", stat);
268 		break;
269 
270 	default:
271 		stat = IDMAP_ERR_ARG;
272 		break;
273 	}
274 
275 	return (stat);
276 }
277 
278 /*
279  * smb_idmap_batch_getsid
280  *
281  * Queue a request to map the given UID/GID to a SID.
282  *
283  * sim->sim_domsid and sim->sim_rid will contain the mapping
284  * result upon successful process of the batched request.
285  * Stash the type for error reporting (caller saves the ID).
286  *
287  * NB: sim_domsid allocated by strdup, here or in libidmap
288  */
289 idmap_stat
290 smb_idmap_batch_getsid(idmap_get_handle_t *idmaph, smb_idmap_t *sim,
291     uid_t id, int idtype)
292 {
293 	idmap_stat stat;
294 	int flag = 0;
295 
296 	if (!idmaph || !sim)
297 		return (IDMAP_ERR_ARG);
298 
299 	sim->sim_idtype = idtype;
300 	switch (idtype) {
301 	case SMB_IDMAP_USER:
302 		stat = idmap_get_sidbyuid(idmaph, id, flag,
303 		    &sim->sim_domsid, &sim->sim_rid, &sim->sim_stat);
304 		smb_idmap_check("idmap_get_sidbyuid", stat);
305 		break;
306 
307 	case SMB_IDMAP_GROUP:
308 		stat = idmap_get_sidbygid(idmaph, id, flag,
309 		    &sim->sim_domsid, &sim->sim_rid, &sim->sim_stat);
310 		smb_idmap_check("idmap_get_sidbygid", stat);
311 		break;
312 
313 	case SMB_IDMAP_OWNERAT:
314 		/* Current Owner S-1-5-32-766 */
315 		sim->sim_domsid = strdup(NT_BUILTIN_DOMAIN_SIDSTR);
316 		sim->sim_rid = SECURITY_CURRENT_OWNER_RID;
317 		sim->sim_stat = IDMAP_SUCCESS;
318 		stat = IDMAP_SUCCESS;
319 		break;
320 
321 	case SMB_IDMAP_GROUPAT:
322 		/* Current Group S-1-5-32-767 */
323 		sim->sim_domsid = strdup(NT_BUILTIN_DOMAIN_SIDSTR);
324 		sim->sim_rid = SECURITY_CURRENT_GROUP_RID;
325 		sim->sim_stat = IDMAP_SUCCESS;
326 		stat = IDMAP_SUCCESS;
327 		break;
328 
329 	case SMB_IDMAP_EVERYONE:
330 		/* Everyone S-1-1-0 */
331 		sim->sim_domsid = strdup(NT_WORLD_AUTH_SIDSTR);
332 		sim->sim_rid = 0;
333 		sim->sim_stat = IDMAP_SUCCESS;
334 		stat = IDMAP_SUCCESS;
335 		break;
336 
337 	default:
338 		ASSERT(0);
339 		return (IDMAP_ERR_ARG);
340 	}
341 
342 	return (stat);
343 }
344 
345 /*
346  * smb_idmap_batch_getmappings
347  *
348  * trigger ID mapping service to get the mappings for queued
349  * requests.
350  *
351  * Checks the result of all the queued requests.
352  */
353 idmap_stat
354 smb_idmap_batch_getmappings(smb_idmap_batch_t *sib,
355     smb_idmap_batch_errcb_t errcb)
356 {
357 	idmap_stat stat = IDMAP_SUCCESS;
358 	smb_idmap_t *sim;
359 	int i;
360 
361 	if ((stat = idmap_get_mappings(sib->sib_idmaph)) != IDMAP_SUCCESS) {
362 		smb_idmap_check("idmap_get_mappings", stat);
363 		return (stat);
364 	}
365 
366 	/*
367 	 * Check the status for all the queued requests
368 	 */
369 	for (i = 0, sim = sib->sib_maps; i < sib->sib_nmap; i++, sim++) {
370 		if (sim->sim_stat != IDMAP_SUCCESS) {
371 			sib->sib_nerr++;
372 			if (errcb != NULL)
373 				errcb(sib, sim);
374 			if ((sib->sib_flags & SMB_IDMAP_SKIP_ERRS) == 0) {
375 				return (sim->sim_stat);
376 			}
377 		}
378 	}
379 
380 	if (smb_idmap_batch_binsid(sib) != 0)
381 		stat = IDMAP_ERR_OTHER;
382 
383 	return (stat);
384 }
385 
386 /*
387  * smb_idmap_batch_binsid
388  *
389  * Convert sidrids to binary sids
390  *
391  * Returns 0 if successful and non-zero upon failure.
392  */
393 static int
394 smb_idmap_batch_binsid(smb_idmap_batch_t *sib)
395 {
396 	smb_sid_t *sid;
397 	smb_idmap_t *sim;
398 	int i;
399 
400 	if (sib->sib_flags & SMB_IDMAP_SID2ID)
401 		/* This operation is not required */
402 		return (0);
403 
404 	sim = sib->sib_maps;
405 	for (i = 0; i < sib->sib_nmap; sim++, i++) {
406 		ASSERT(sim->sim_domsid != NULL);
407 		if (sim->sim_domsid == NULL)
408 			return (-1);
409 
410 		sid = smb_sid_fromstr(sim->sim_domsid);
411 		if (sid == NULL)
412 			return (-1);
413 
414 		sim->sim_sid = smb_sid_splice(sid, sim->sim_rid);
415 		smb_sid_free(sid);
416 	}
417 
418 	return (0);
419 }
420