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