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