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 2008 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 237 (void) mutex_lock(&ndr_handle_lock); 238 hd->nh_next = ndr_handle_list; 239 ndr_handle_list = hd; 240 (void) mutex_unlock(&ndr_handle_lock); 241 242 return (&hd->nh_id); 243 } 244 245 /* 246 * Remove a handle from the global list and free it. 247 */ 248 void 249 ndr_hdfree(const ndr_xa_t *xa, const ndr_hdid_t *id) 250 { 251 ndr_service_t *svc = xa->binding->service; 252 ndr_handle_t *hd; 253 ndr_handle_t **pphd; 254 255 assert(id); 256 257 (void) mutex_lock(&ndr_handle_lock); 258 pphd = &ndr_handle_list; 259 260 while (*pphd) { 261 hd = *pphd; 262 263 if (bcmp(&hd->nh_id, id, sizeof (ndr_hdid_t)) == 0) { 264 if (hd->nh_svc == svc) { 265 *pphd = hd->nh_next; 266 free(hd); 267 } 268 break; 269 } 270 271 pphd = &(*pphd)->nh_next; 272 } 273 274 (void) mutex_unlock(&ndr_handle_lock); 275 } 276 277 /* 278 * Lookup a handle by id. If the handle is in the list and it matches 279 * the specified service, a pointer to it is returned. Otherwise a null 280 * pointer is returned. 281 */ 282 ndr_handle_t * 283 ndr_hdlookup(const ndr_xa_t *xa, const ndr_hdid_t *id) 284 { 285 ndr_service_t *svc = xa->binding->service; 286 ndr_handle_t *hd; 287 288 assert(id); 289 (void) mutex_lock(&ndr_handle_lock); 290 hd = ndr_handle_list; 291 292 while (hd) { 293 if (bcmp(&hd->nh_id, id, sizeof (ndr_hdid_t)) == 0) { 294 if (hd->nh_svc != svc) 295 break; 296 (void) mutex_unlock(&ndr_handle_lock); 297 return (hd); 298 } 299 300 hd = hd->nh_next; 301 } 302 303 (void) mutex_unlock(&ndr_handle_lock); 304 return (NULL); 305 } 306 307 /* 308 * Called when a pipe is closed to release any associated handles. 309 */ 310 void 311 ndr_hdclose(int fid) 312 { 313 ndr_handle_t *hd; 314 ndr_handle_t **pphd; 315 316 (void) mutex_lock(&ndr_handle_lock); 317 pphd = &ndr_handle_list; 318 319 while (*pphd) { 320 hd = *pphd; 321 322 if (hd->nh_fid == fid) { 323 *pphd = hd->nh_next; 324 free(hd); 325 continue; 326 } 327 328 pphd = &(*pphd)->nh_next; 329 } 330 331 (void) mutex_unlock(&ndr_handle_lock); 332 } 333 334 /* 335 * Convert a UUID to a string. 336 */ 337 void 338 ndr_uuid_unparse(ndr_uuid_t *uuid, char *out) 339 { 340 (void) sprintf(out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 341 uuid->data1, uuid->data2, uuid->data3, 342 uuid->data4[0], uuid->data4[1], 343 uuid->data4[2], uuid->data4[3], 344 uuid->data4[4], uuid->data4[5], 345 uuid->data4[6], uuid->data4[7]); 346 } 347 348 /* 349 * Convert a string to a UUID. 350 */ 351 int 352 ndr_uuid_parse(char *in, ndr_uuid_t *uuid) 353 { 354 char *p = in; 355 char *q; 356 char buf[4]; 357 int i; 358 359 if (strlen(in) != UUID_PRINTABLE_STRING_LENGTH - 1) 360 return (-1); 361 362 uuid->data1 = strtoul(p, &p, 16); 363 if (*p != '-') 364 return (-1); 365 p++; 366 367 uuid->data2 = strtol(p, &p, 16); 368 if (*p != '-') 369 return (-1); 370 p++; 371 372 uuid->data3 = strtol(p, &p, 16); 373 if (*p != '-') 374 return (-1); 375 p++; 376 377 for (i = 0; i < 8; i++) { 378 if (*p == '-') 379 p++; 380 381 if (p[0] == 0 || p[1] == 0) 382 return (-1); 383 384 buf[0] = *p++; 385 buf[1] = *p++; 386 buf[2] = 0; 387 uuid->data4[i] = strtol(buf, &q, 16); 388 if (*q != 0) 389 return (-1); 390 } 391 392 if (*p != 0) 393 return (-1); 394 395 return (0); 396 } 397 398 void 399 ndr_svc_binding_pool_init(ndr_binding_t **headpp, ndr_binding_t pool[], 400 int n_pool) 401 { 402 ndr_binding_t *head = NULL; 403 int ix; 404 405 for (ix = n_pool - 1; ix >= 0; ix--) { 406 pool[ix].next = head; 407 pool[ix].service = NULL; 408 pool[ix].p_cont_id = 0xffff; 409 pool[ix].instance_specific = 0; 410 head = &pool[ix]; 411 } 412 413 *headpp = head; 414 } 415 416 ndr_binding_t * 417 ndr_svc_find_binding(ndr_xa_t *mxa, ndr_p_context_id_t p_cont_id) 418 { 419 ndr_binding_t *mbind; 420 421 for (mbind = mxa->binding_list; mbind; mbind = mbind->next) { 422 if (mbind->service != NULL && 423 mbind->which_side == NDR_BIND_SIDE_SERVER && 424 mbind->p_cont_id == p_cont_id) 425 break; 426 } 427 428 return (mbind); 429 } 430 431 ndr_binding_t * 432 ndr_svc_new_binding(ndr_xa_t *mxa) 433 { 434 ndr_binding_t *mbind; 435 436 for (mbind = mxa->binding_list; mbind; mbind = mbind->next) { 437 if (mbind->service == NULL) 438 break; 439 } 440 441 return (mbind); 442 } 443 444 /* 445 * Move bytes between a buffer and a uio structure. 446 * The transfer direction is controlled by rw: 447 * UIO_READ: transfer from buf to uio 448 * UIO_WRITE: transfer from uio to buf 449 * 450 * Returns the number of bytes moved. 451 */ 452 ssize_t 453 ndr_uiomove(caddr_t buf, size_t buflen, enum uio_rw rw, struct uio *uio) 454 { 455 struct iovec *iov; 456 int reading = (rw == UIO_READ); 457 size_t nbytes; 458 size_t nxfer = 0; 459 460 assert(rw == UIO_READ || rw == UIO_WRITE); 461 462 while (buflen && uio->uio_resid && uio->uio_iovcnt) { 463 iov = uio->uio_iov; 464 if ((nbytes = iov->iov_len) == 0) { 465 uio->uio_iov++; 466 uio->uio_iovcnt--; 467 continue; 468 } 469 470 if (nbytes > buflen) 471 nbytes = buflen; 472 473 if (reading) 474 bcopy(buf, iov->iov_base, nbytes); 475 else 476 bcopy(iov->iov_base, buf, nbytes); 477 478 iov->iov_base += nbytes; 479 iov->iov_len -= nbytes; 480 uio->uio_resid -= nbytes; 481 uio->uio_offset += nbytes; 482 buf += nbytes; 483 buflen -= nbytes; 484 nxfer += nbytes; 485 } 486 487 return (nxfer); 488 } 489