xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_idmap.c (revision 24f5a37652e188ebdcdd6da454511686935025df)
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 2013 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  *	uts/common/fs/smbsrv/smb_idmap.c (smbsrv kmod)
32  *	lib/smbsrv/libfksmbsrv/common/fksmb_idmap.c (libfksmbsrv)
33  *	lib/smbsrv/libsmb/common/smb_idmap.c (libsmb)
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  *	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 	if (sim.sim_stat != IDMAP_SUCCESS)
108 		return (sim.sim_stat);
109 
110 	if (sim.sim_domsid == NULL)
111 		return (IDMAP_ERR_NOMAPPING);
112 
113 	sim.sim_sid = smb_sid_fromstr(sim.sim_domsid);
114 	if (sim.sim_sid == NULL)
115 		return (IDMAP_ERR_INTERNAL);
116 
117 	*sid = smb_sid_splice(sim.sim_sid, sim.sim_rid);
118 	smb_sid_free(sim.sim_sid);
119 	if (*sid == NULL)
120 		sim.sim_stat = IDMAP_ERR_INTERNAL;
121 
122 	return (sim.sim_stat);
123 }
124 
125 /*
126  * smb_idmap_getid
127  *
128  * Maps the given Windows SID to a Unix ID using the
129  * simple mapping API.
130  */
131 idmap_stat
132 smb_idmap_getid(smb_sid_t *sid, uid_t *id, int *idtype)
133 {
134 	smb_idmap_t sim;
135 	char sidstr[SMB_SID_STRSZ];
136 
137 	smb_sid_tostr(sid, sidstr);
138 	if (smb_sid_splitstr(sidstr, &sim.sim_rid) != 0)
139 		return (IDMAP_ERR_SID);
140 	sim.sim_domsid = sidstr;
141 	sim.sim_id = id;
142 
143 	switch (*idtype) {
144 	case SMB_IDMAP_USER:
145 		sim.sim_stat = kidmap_getuidbysid(global_zone, sim.sim_domsid,
146 		    sim.sim_rid, sim.sim_id);
147 		break;
148 
149 	case SMB_IDMAP_GROUP:
150 		sim.sim_stat = kidmap_getgidbysid(global_zone, sim.sim_domsid,
151 		    sim.sim_rid, sim.sim_id);
152 		break;
153 
154 	case SMB_IDMAP_UNKNOWN:
155 		sim.sim_stat = kidmap_getpidbysid(global_zone, sim.sim_domsid,
156 		    sim.sim_rid, sim.sim_id, &sim.sim_idtype);
157 		break;
158 
159 	default:
160 		ASSERT(0);
161 		return (IDMAP_ERR_ARG);
162 	}
163 
164 	*idtype = sim.sim_idtype;
165 
166 	return (sim.sim_stat);
167 }
168 
169 /*
170  * smb_idmap_batch_create
171  *
172  * Creates and initializes the context for batch ID mapping.
173  */
174 idmap_stat
175 smb_idmap_batch_create(smb_idmap_batch_t *sib, uint16_t nmap, int flags)
176 {
177 	ASSERT(sib);
178 
179 	bzero(sib, sizeof (smb_idmap_batch_t));
180 
181 	sib->sib_idmaph = kidmap_get_create(global_zone);
182 
183 	sib->sib_flags = flags;
184 	sib->sib_nmap = nmap;
185 	sib->sib_size = nmap * sizeof (smb_idmap_t);
186 	sib->sib_maps = kmem_zalloc(sib->sib_size, KM_SLEEP);
187 
188 	return (IDMAP_SUCCESS);
189 }
190 
191 /*
192  * smb_idmap_batch_destroy
193  *
194  * Frees the batch ID mapping context.
195  * If ID mapping is Solaris -> Windows it frees memories
196  * allocated for binary SIDs.
197  */
198 void
199 smb_idmap_batch_destroy(smb_idmap_batch_t *sib)
200 {
201 	char *domsid;
202 	int i;
203 
204 	ASSERT(sib);
205 	ASSERT(sib->sib_maps);
206 
207 	if (sib->sib_idmaph)
208 		kidmap_get_destroy(sib->sib_idmaph);
209 
210 	if (sib->sib_flags & SMB_IDMAP_ID2SID) {
211 		/*
212 		 * SIDs are allocated only when mapping
213 		 * UID/GID to SIDs
214 		 */
215 		for (i = 0; i < sib->sib_nmap; i++)
216 			smb_sid_free(sib->sib_maps[i].sim_sid);
217 	} else if (sib->sib_flags & SMB_IDMAP_SID2ID) {
218 		/*
219 		 * SID prefixes are allocated only when mapping
220 		 * SIDs to UID/GID
221 		 */
222 		for (i = 0; i < sib->sib_nmap; i++) {
223 			domsid = sib->sib_maps[i].sim_domsid;
224 			if (domsid)
225 				smb_mem_free(domsid);
226 		}
227 	}
228 
229 	if (sib->sib_size && sib->sib_maps)
230 		kmem_free(sib->sib_maps, sib->sib_size);
231 }
232 
233 /*
234  * smb_idmap_batch_getid
235  *
236  * Queue a request to map the given SID to a UID or GID.
237  *
238  * sim->sim_id should point to variable that's supposed to
239  * hold the returned UID/GID. This needs to be setup by caller
240  * of this function.
241  *
242  * If requested ID type is known, it's passed as 'idtype',
243  * if it's unknown it'll be returned in sim->sim_idtype.
244  */
245 idmap_stat
246 smb_idmap_batch_getid(idmap_get_handle_t *idmaph, smb_idmap_t *sim,
247     smb_sid_t *sid, int idtype)
248 {
249 	char strsid[SMB_SID_STRSZ];
250 	idmap_stat idm_stat;
251 
252 	ASSERT(idmaph);
253 	ASSERT(sim);
254 	ASSERT(sid);
255 
256 	smb_sid_tostr(sid, strsid);
257 	if (smb_sid_splitstr(strsid, &sim->sim_rid) != 0)
258 		return (IDMAP_ERR_SID);
259 	sim->sim_domsid = smb_mem_strdup(strsid);
260 
261 	switch (idtype) {
262 	case SMB_IDMAP_USER:
263 		idm_stat = kidmap_batch_getuidbysid(idmaph, sim->sim_domsid,
264 		    sim->sim_rid, sim->sim_id, &sim->sim_stat);
265 		break;
266 
267 	case SMB_IDMAP_GROUP:
268 		idm_stat = kidmap_batch_getgidbysid(idmaph, sim->sim_domsid,
269 		    sim->sim_rid, sim->sim_id, &sim->sim_stat);
270 		break;
271 
272 	case SMB_IDMAP_UNKNOWN:
273 		idm_stat = kidmap_batch_getpidbysid(idmaph, sim->sim_domsid,
274 		    sim->sim_rid, sim->sim_id, &sim->sim_idtype,
275 		    &sim->sim_stat);
276 		break;
277 
278 	default:
279 		ASSERT(0);
280 		return (IDMAP_ERR_ARG);
281 	}
282 
283 	return (idm_stat);
284 }
285 
286 /*
287  * smb_idmap_batch_getsid
288  *
289  * Queue a request to map the given UID/GID to a SID.
290  *
291  * sim->sim_domsid and sim->sim_rid will contain the mapping
292  * result upon successful process of the batched request.
293  */
294 idmap_stat
295 smb_idmap_batch_getsid(idmap_get_handle_t *idmaph, smb_idmap_t *sim,
296     uid_t id, int idtype)
297 {
298 	idmap_stat idm_stat;
299 
300 	switch (idtype) {
301 	case SMB_IDMAP_USER:
302 		idm_stat = kidmap_batch_getsidbyuid(idmaph, id,
303 		    (const char **)&sim->sim_domsid, &sim->sim_rid,
304 		    &sim->sim_stat);
305 		break;
306 
307 	case SMB_IDMAP_GROUP:
308 		idm_stat = kidmap_batch_getsidbygid(idmaph, id,
309 		    (const char **)&sim->sim_domsid, &sim->sim_rid,
310 		    &sim->sim_stat);
311 		break;
312 
313 	case SMB_IDMAP_OWNERAT:
314 		/* Current Owner S-1-5-32-766 */
315 		sim->sim_domsid = NT_BUILTIN_DOMAIN_SIDSTR;
316 		sim->sim_rid = SECURITY_CURRENT_OWNER_RID;
317 		sim->sim_stat = IDMAP_SUCCESS;
318 		idm_stat = IDMAP_SUCCESS;
319 		break;
320 
321 	case SMB_IDMAP_GROUPAT:
322 		/* Current Group S-1-5-32-767 */
323 		sim->sim_domsid = NT_BUILTIN_DOMAIN_SIDSTR;
324 		sim->sim_rid = SECURITY_CURRENT_GROUP_RID;
325 		sim->sim_stat = IDMAP_SUCCESS;
326 		idm_stat = IDMAP_SUCCESS;
327 		break;
328 
329 	case SMB_IDMAP_EVERYONE:
330 		/* Everyone S-1-1-0 */
331 		sim->sim_domsid = NT_WORLD_AUTH_SIDSTR;
332 		sim->sim_rid = 0;
333 		sim->sim_stat = IDMAP_SUCCESS;
334 		idm_stat = IDMAP_SUCCESS;
335 		break;
336 
337 	default:
338 		ASSERT(0);
339 		return (IDMAP_ERR_ARG);
340 	}
341 
342 	return (idm_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  * If this is a Solaris -> Windows mapping it generates
353  * binary SIDs from returned (domsid, rid) pairs.
354  */
355 idmap_stat
356 smb_idmap_batch_getmappings(smb_idmap_batch_t *sib)
357 {
358 	idmap_stat idm_stat = IDMAP_SUCCESS;
359 	int i;
360 
361 	idm_stat = kidmap_get_mappings(sib->sib_idmaph);
362 	if (idm_stat != IDMAP_SUCCESS)
363 		return (idm_stat);
364 
365 	/*
366 	 * Check the status for all the queued requests
367 	 */
368 	for (i = 0; i < sib->sib_nmap; i++) {
369 		if (sib->sib_maps[i].sim_stat != IDMAP_SUCCESS)
370 			return (sib->sib_maps[i].sim_stat);
371 	}
372 
373 	if (smb_idmap_batch_binsid(sib) != 0)
374 		idm_stat = IDMAP_ERR_OTHER;
375 
376 	return (idm_stat);
377 }
378 
379 /*
380  * smb_idmap_batch_binsid
381  *
382  * Convert sidrids to binary sids
383  *
384  * Returns 0 if successful and non-zero upon failure.
385  */
386 static int
387 smb_idmap_batch_binsid(smb_idmap_batch_t *sib)
388 {
389 	smb_sid_t *sid;
390 	smb_idmap_t *sim;
391 	int i;
392 
393 	if (sib->sib_flags & SMB_IDMAP_SID2ID)
394 		/* This operation is not required */
395 		return (0);
396 
397 	sim = sib->sib_maps;
398 	for (i = 0; i < sib->sib_nmap; sim++, i++) {
399 		ASSERT(sim->sim_domsid);
400 		if (sim->sim_domsid == NULL)
401 			return (1);
402 
403 		if ((sid = smb_sid_fromstr(sim->sim_domsid)) == NULL)
404 			return (1);
405 
406 		sim->sim_sid = smb_sid_splice(sid, sim->sim_rid);
407 		smb_sid_free(sid);
408 	}
409 
410 	return (0);
411 }
412