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