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