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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
26 */
27
28 #include <uuid/uuid.h>
29 #include <ctype.h>
30 #include <synch.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <assert.h>
36
37 #include <smbsrv/libsmb.h>
38 #include <smbsrv/libmlrpc.h>
39
40
41 /*
42 * Global list of allocated handles. Handles are used in various
43 * server-side RPC functions: typically, issued when a service is
44 * opened and obsoleted when it is closed. Clients should treat
45 * handles as opaque data.
46 */
47 static ndr_handle_t *ndr_handle_list;
48 static mutex_t ndr_handle_lock;
49
50 /*
51 * Table of registered services.
52 */
53 #define NDR_MAX_SERVICES 32
54 static ndr_service_t *ndr_services[NDR_MAX_SERVICES];
55
56 /*
57 * Register a service.
58 *
59 * Returns:
60 * 0 Success
61 * -1 Duplicate service
62 * -2 Duplicate name
63 * -3 Table overflow
64 */
65 int
ndr_svc_register(ndr_service_t * svc)66 ndr_svc_register(ndr_service_t *svc)
67 {
68 ndr_service_t *p;
69 int free_slot = -1;
70 int i;
71
72 for (i = 0; i < NDR_MAX_SERVICES; i++) {
73 if ((p = ndr_services[i]) == NULL) {
74 if (free_slot < 0)
75 free_slot = i;
76 continue;
77 }
78
79 if (p == svc)
80 return (-1);
81
82 if (strcasecmp(p->name, svc->name) == 0)
83 return (-2);
84 }
85
86 if (free_slot < 0)
87 return (-3);
88
89 ndr_services[free_slot] = svc;
90 return (0);
91 }
92
93 void
ndr_svc_unregister(ndr_service_t * svc)94 ndr_svc_unregister(ndr_service_t *svc)
95 {
96 int i;
97
98 for (i = 0; i < NDR_MAX_SERVICES; i++) {
99 if (ndr_services[i] == svc)
100 ndr_services[i] = NULL;
101 }
102 }
103
104 ndr_stub_table_t *
ndr_svc_find_stub(ndr_service_t * svc,int opnum)105 ndr_svc_find_stub(ndr_service_t *svc, int opnum)
106 {
107 ndr_stub_table_t *ste;
108
109 for (ste = svc->stub_table; ste->func; ste++) {
110 if (ste->opnum == opnum)
111 return (ste);
112 }
113
114 return (NULL);
115 }
116
117 ndr_service_t *
ndr_svc_lookup_name(const char * name)118 ndr_svc_lookup_name(const char *name)
119 {
120 ndr_service_t *svc;
121 int i;
122
123 for (i = 0; i < NDR_MAX_SERVICES; i++) {
124 if ((svc = ndr_services[i]) == NULL)
125 continue;
126
127 if (strcasecmp(name, svc->name) != 0)
128 continue;
129
130 ndo_printf(0, 0, "%s %s", svc->name, svc->desc);
131 return (svc);
132 }
133
134 return (NULL);
135 }
136
137 ndr_service_t *
ndr_svc_lookup_uuid(ndr_uuid_t * as_uuid,int as_vers,ndr_uuid_t * ts_uuid,int ts_vers)138 ndr_svc_lookup_uuid(ndr_uuid_t *as_uuid, int as_vers,
139 ndr_uuid_t *ts_uuid, int ts_vers)
140 {
141 ndr_service_t *svc;
142 char abstract_syntax[UUID_PRINTABLE_STRING_LENGTH];
143 char transfer_syntax[UUID_PRINTABLE_STRING_LENGTH];
144 int i;
145
146 if (as_uuid)
147 ndr_uuid_unparse(as_uuid, abstract_syntax);
148
149 if (ts_uuid)
150 ndr_uuid_unparse(ts_uuid, transfer_syntax);
151
152 for (i = 0; i < NDR_MAX_SERVICES; i++) {
153 if ((svc = ndr_services[i]) == NULL)
154 continue;
155
156 if (as_uuid) {
157 if (svc->abstract_syntax_uuid == 0)
158 continue;
159
160 if (svc->abstract_syntax_version != as_vers)
161 continue;
162
163 if (strcasecmp(abstract_syntax,
164 svc->abstract_syntax_uuid))
165 continue;
166 }
167
168 if (ts_uuid) {
169 if (svc->transfer_syntax_uuid == 0)
170 continue;
171
172 if (svc->transfer_syntax_version != ts_vers)
173 continue;
174
175 if (strcasecmp(transfer_syntax,
176 svc->transfer_syntax_uuid))
177 continue;
178 }
179
180 ndo_printf(0, 0, "%s %s", svc->name, svc->desc);
181 return (svc);
182 }
183
184 ndo_printf(0, 0, "ndr_svc_lookup_uuid: unknown service");
185 ndo_printf(0, 0, "abstract=%s v%d, transfer=%s v%d",
186 abstract_syntax, as_vers, transfer_syntax, ts_vers);
187 return (NULL);
188 }
189
190 /*
191 * Allocate a handle for use with the server-side RPC functions.
192 *
193 * An arbitrary caller context can be associated with the handle
194 * via data; it will not be dereferenced by the handle API.
195 */
196 ndr_hdid_t *
ndr_hdalloc(const ndr_xa_t * xa,const void * data)197 ndr_hdalloc(const ndr_xa_t *xa, const void *data)
198 {
199 static ndr_hdid_t id;
200 ndr_handle_t *hd;
201 uuid_t uu;
202
203 if ((hd = malloc(sizeof (ndr_handle_t))) == NULL)
204 return (NULL);
205
206 if (id.data2 == 0) {
207 uuid_generate_random(uu);
208 bcopy(uu, &id.data2, sizeof (uuid_t));
209 id.data1 = 0;
210 id.data2 = 0;
211 }
212
213 ++id.data2;
214
215 bcopy(&id, &hd->nh_id, sizeof (ndr_hdid_t));
216 hd->nh_pipe = xa->pipe;
217 hd->nh_svc = xa->binding->service;
218 hd->nh_data = (void *)data;
219 hd->nh_data_free = NULL;
220
221 (void) mutex_lock(&ndr_handle_lock);
222 hd->nh_next = ndr_handle_list;
223 ndr_handle_list = hd;
224 (void) mutex_unlock(&ndr_handle_lock);
225
226 return (&hd->nh_id);
227 }
228
229 /*
230 * Remove a handle from the global list and free it.
231 */
232 void
ndr_hdfree(const ndr_xa_t * xa,const ndr_hdid_t * id)233 ndr_hdfree(const ndr_xa_t *xa, const ndr_hdid_t *id)
234 {
235 ndr_service_t *svc = xa->binding->service;
236 ndr_handle_t *hd;
237 ndr_handle_t **pphd;
238
239 assert(id);
240
241 (void) mutex_lock(&ndr_handle_lock);
242 pphd = &ndr_handle_list;
243
244 while (*pphd) {
245 hd = *pphd;
246
247 if (bcmp(&hd->nh_id, id, sizeof (ndr_hdid_t)) == 0) {
248 if (hd->nh_svc == svc) {
249 *pphd = hd->nh_next;
250 free(hd);
251 }
252 break;
253 }
254
255 pphd = &(*pphd)->nh_next;
256 }
257
258 (void) mutex_unlock(&ndr_handle_lock);
259 }
260
261 /*
262 * Lookup a handle by id. If the handle is in the list and it matches
263 * the specified service, a pointer to it is returned. Otherwise a null
264 * pointer is returned.
265 */
266 ndr_handle_t *
ndr_hdlookup(const ndr_xa_t * xa,const ndr_hdid_t * id)267 ndr_hdlookup(const ndr_xa_t *xa, const ndr_hdid_t *id)
268 {
269 ndr_service_t *svc = xa->binding->service;
270 ndr_handle_t *hd;
271
272 assert(id);
273 (void) mutex_lock(&ndr_handle_lock);
274 hd = ndr_handle_list;
275
276 while (hd) {
277 if (bcmp(&hd->nh_id, id, sizeof (ndr_hdid_t)) == 0) {
278 if (hd->nh_svc != svc)
279 break;
280 (void) mutex_unlock(&ndr_handle_lock);
281 return (hd);
282 }
283
284 hd = hd->nh_next;
285 }
286
287 (void) mutex_unlock(&ndr_handle_lock);
288 return (NULL);
289 }
290
291 /*
292 * Called when a pipe is closed to release any associated handles.
293 */
294 void
ndr_hdclose(ndr_pipe_t * pipe)295 ndr_hdclose(ndr_pipe_t *pipe)
296 {
297 ndr_handle_t *hd;
298 ndr_handle_t **pphd;
299
300 (void) mutex_lock(&ndr_handle_lock);
301 pphd = &ndr_handle_list;
302
303 while (*pphd) {
304 hd = *pphd;
305
306 if (hd->nh_pipe == pipe) {
307 *pphd = hd->nh_next;
308
309 if (hd->nh_data_free)
310 (*hd->nh_data_free)(hd->nh_data);
311
312 free(hd);
313 continue;
314 }
315
316 pphd = &(*pphd)->nh_next;
317 }
318
319 (void) mutex_unlock(&ndr_handle_lock);
320 }
321
322 /*
323 * Convert a UUID to a string.
324 */
325 void
ndr_uuid_unparse(ndr_uuid_t * uuid,char * out)326 ndr_uuid_unparse(ndr_uuid_t *uuid, char *out)
327 {
328 (void) sprintf(out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
329 uuid->data1, uuid->data2, uuid->data3,
330 uuid->data4[0], uuid->data4[1],
331 uuid->data4[2], uuid->data4[3],
332 uuid->data4[4], uuid->data4[5],
333 uuid->data4[6], uuid->data4[7]);
334 }
335
336 /*
337 * Convert a string to a UUID.
338 */
339 int
ndr_uuid_parse(char * in,ndr_uuid_t * uuid)340 ndr_uuid_parse(char *in, ndr_uuid_t *uuid)
341 {
342 char *p = in;
343 char *q;
344 char buf[4];
345 int i;
346
347 if (strlen(in) != UUID_PRINTABLE_STRING_LENGTH - 1)
348 return (-1);
349
350 uuid->data1 = strtoul(p, &p, 16);
351 if (*p != '-')
352 return (-1);
353 p++;
354
355 uuid->data2 = strtol(p, &p, 16);
356 if (*p != '-')
357 return (-1);
358 p++;
359
360 uuid->data3 = strtol(p, &p, 16);
361 if (*p != '-')
362 return (-1);
363 p++;
364
365 for (i = 0; i < 8; i++) {
366 if (*p == '-')
367 p++;
368
369 if (p[0] == 0 || p[1] == 0)
370 return (-1);
371
372 buf[0] = *p++;
373 buf[1] = *p++;
374 buf[2] = 0;
375 uuid->data4[i] = strtol(buf, &q, 16);
376 if (*q != 0)
377 return (-1);
378 }
379
380 if (*p != 0)
381 return (-1);
382
383 return (0);
384 }
385
386 void
ndr_svc_binding_pool_init(ndr_binding_t ** headpp,ndr_binding_t pool[],int n_pool)387 ndr_svc_binding_pool_init(ndr_binding_t **headpp, ndr_binding_t pool[],
388 int n_pool)
389 {
390 ndr_binding_t *head = NULL;
391 int ix;
392
393 for (ix = n_pool - 1; ix >= 0; ix--) {
394 pool[ix].next = head;
395 pool[ix].service = NULL;
396 pool[ix].p_cont_id = 0xffff;
397 pool[ix].instance_specific = 0;
398 head = &pool[ix];
399 }
400
401 *headpp = head;
402 }
403
404 ndr_binding_t *
ndr_svc_find_binding(ndr_xa_t * mxa,ndr_p_context_id_t p_cont_id)405 ndr_svc_find_binding(ndr_xa_t *mxa, ndr_p_context_id_t p_cont_id)
406 {
407 ndr_binding_t *mbind;
408
409 for (mbind = mxa->binding_list; mbind; mbind = mbind->next) {
410 if (mbind->service != NULL &&
411 mbind->which_side == NDR_BIND_SIDE_SERVER &&
412 mbind->p_cont_id == p_cont_id)
413 break;
414 }
415
416 return (mbind);
417 }
418
419 ndr_binding_t *
ndr_svc_new_binding(ndr_xa_t * mxa)420 ndr_svc_new_binding(ndr_xa_t *mxa)
421 {
422 ndr_binding_t *mbind;
423
424 for (mbind = mxa->binding_list; mbind; mbind = mbind->next) {
425 if (mbind->service == NULL)
426 break;
427 }
428
429 return (mbind);
430 }
431
432 /*
433 * Move bytes between a buffer and a uio structure.
434 * The transfer direction is controlled by rw:
435 * UIO_READ: transfer from buf to uio
436 * UIO_WRITE: transfer from uio to buf
437 *
438 * Returns the number of bytes moved.
439 */
440 ssize_t
ndr_uiomove(caddr_t buf,size_t buflen,enum uio_rw rw,struct uio * uio)441 ndr_uiomove(caddr_t buf, size_t buflen, enum uio_rw rw, struct uio *uio)
442 {
443 struct iovec *iov;
444 int reading = (rw == UIO_READ);
445 size_t nbytes;
446 size_t nxfer = 0;
447
448 assert(rw == UIO_READ || rw == UIO_WRITE);
449
450 while (buflen && uio->uio_resid && uio->uio_iovcnt) {
451 iov = uio->uio_iov;
452 if ((nbytes = iov->iov_len) == 0) {
453 uio->uio_iov++;
454 uio->uio_iovcnt--;
455 continue;
456 }
457
458 if (nbytes > buflen)
459 nbytes = buflen;
460
461 if (reading)
462 bcopy(buf, iov->iov_base, nbytes);
463 else
464 bcopy(iov->iov_base, buf, nbytes);
465
466 iov->iov_base += nbytes;
467 iov->iov_len -= nbytes;
468 uio->uio_resid -= nbytes;
469 uio->uio_offset += nbytes;
470 buf += nbytes;
471 buflen -= nbytes;
472 nxfer += nbytes;
473 }
474
475 return (nxfer);
476 }
477