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