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