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 2019 Nexenta Systems, Inc. All rights reserved. 24 */ 25 26 #include <assert.h> 27 #include <syslog.h> 28 #include <door.h> 29 #include <fcntl.h> 30 #include <string.h> 31 #include <strings.h> 32 #include <stdlib.h> 33 #include <unistd.h> 34 #include <errno.h> 35 #include <sys/mman.h> 36 #include <smb/wintypes.h> 37 #include <smbsrv/libsmb.h> 38 #include <smbsrv/smb_door.h> 39 40 static int smb_door_call(uint32_t, void *, xdrproc_t, void *, xdrproc_t); 41 static int smb_door_call_private(int, smb_doorarg_t *); 42 static int smb_door_encode(smb_doorarg_t *, uint32_t); 43 static int smb_door_decode(smb_doorarg_t *); 44 static void smb_door_sethdr(smb_doorhdr_t *, uint32_t, uint32_t); 45 static boolean_t smb_door_chkhdr(smb_doorarg_t *, smb_doorhdr_t *); 46 static void smb_door_free(door_arg_t *arg); 47 static int smb_lookup_name_int(const char *name, sid_type_t sidtype, 48 lsa_account_t *acct, int); 49 static int smb_lookup_sid_int(const char *sid, lsa_account_t *acct, int); 50 51 /* 52 * Given a SID, make a door call to get the associated name. 53 * 54 * Returns 0 if the door call is successful, otherwise -1. 55 * 56 * If 0 is returned, the lookup result will be available in a_status. 57 * NT_STATUS_SUCCESS The SID was mapped to a name. 58 * NT_STATUS_NONE_MAPPED The SID could not be mapped to a name. 59 */ 60 int 61 smb_lookup_sid(const char *sid, lsa_account_t *acct) 62 { 63 return (smb_lookup_sid_int(sid, acct, SMB_DR_LOOKUP_SID)); 64 } 65 /* 66 * Variant of smb_lookup_sid to do a "local-only" lookup. 67 */ 68 int 69 smb_lookup_lsid(const char *sid, lsa_account_t *acct) 70 { 71 return (smb_lookup_sid_int(sid, acct, SMB_DR_LOOKUP_LSID)); 72 } 73 74 static int 75 smb_lookup_sid_int(const char *sid, lsa_account_t *acct, int dop) 76 { 77 int rc; 78 79 assert((sid != NULL) && (acct != NULL)); 80 81 bzero(acct, sizeof (lsa_account_t)); 82 (void) strlcpy(acct->a_sid, sid, SMB_SID_STRSZ); 83 84 rc = smb_door_call(dop, acct, lsa_account_xdr, 85 acct, lsa_account_xdr); 86 87 if (rc != 0) 88 syslog(LOG_DEBUG, "smb_lookup_sid: %m"); 89 return (rc); 90 } 91 92 /* 93 * Given a name, make a door call to get the associated SID. 94 * 95 * Returns 0 if the door call is successful, otherwise -1. 96 * 97 * If 0 is returned, the lookup result will be available in a_status. 98 * NT_STATUS_SUCCESS The name was mapped to a SID. 99 * NT_STATUS_NONE_MAPPED The name could not be mapped to a SID. 100 */ 101 int 102 smb_lookup_name(const char *name, sid_type_t sidtype, lsa_account_t *acct) 103 { 104 return (smb_lookup_name_int(name, sidtype, acct, SMB_DR_LOOKUP_NAME)); 105 } 106 107 int 108 smb_lookup_lname(const char *name, sid_type_t sidtype, lsa_account_t *acct) 109 { 110 return (smb_lookup_name_int(name, sidtype, acct, SMB_DR_LOOKUP_LNAME)); 111 } 112 113 static int 114 smb_lookup_name_int(const char *name, sid_type_t sidtype, lsa_account_t *acct, 115 int dop) 116 { 117 char tmp[MAXNAMELEN]; 118 char *dp = NULL; 119 char *np = NULL; 120 int rc; 121 122 assert((name != NULL) && (acct != NULL)); 123 124 (void) strlcpy(tmp, name, MAXNAMELEN); 125 smb_name_parse(tmp, &np, &dp); 126 127 bzero(acct, sizeof (lsa_account_t)); 128 acct->a_sidtype = sidtype; 129 130 if (dp != NULL && np != NULL) { 131 (void) strlcpy(acct->a_domain, dp, MAXNAMELEN); 132 (void) strlcpy(acct->a_name, np, MAXNAMELEN); 133 } else { 134 (void) strlcpy(acct->a_name, name, MAXNAMELEN); 135 } 136 137 rc = smb_door_call(dop, acct, lsa_account_xdr, 138 acct, lsa_account_xdr); 139 140 if (rc != 0) 141 syslog(LOG_DEBUG, "smb_lookup_name: %m"); 142 return (rc); 143 } 144 145 int 146 smb_join(smb_joininfo_t *jdi, smb_joinres_t *jres) 147 { 148 int rc; 149 150 rc = smb_door_call(SMB_DR_JOIN, jdi, smb_joininfo_xdr, 151 jres, smb_joinres_xdr); 152 153 if (rc != 0) { 154 /* 155 * This usually means the SMB service is not running. 156 */ 157 syslog(LOG_DEBUG, "smb_join: %m"); 158 jres->status = NT_STATUS_SERVER_DISABLED; 159 return (rc); 160 } 161 162 return (0); 163 } 164 165 /* 166 * Get information about the Domain Controller in the joined resource domain. 167 * 168 * Returns NT status codes. 169 */ 170 uint32_t 171 smb_get_dcinfo(char *namebuf, uint32_t namebuflen, smb_inaddr_t *ipaddr) 172 { 173 smb_string_t dcname; 174 struct hostent *h; 175 int rc; 176 177 assert((namebuf != NULL) && (namebuflen != 0)); 178 *namebuf = '\0'; 179 bzero(&dcname, sizeof (smb_string_t)); 180 181 rc = smb_door_call(SMB_DR_GET_DCINFO, NULL, NULL, 182 &dcname, smb_string_xdr); 183 184 if (rc != 0) { 185 syslog(LOG_DEBUG, "smb_get_dcinfo: %m"); 186 if (dcname.buf) 187 xdr_free(smb_string_xdr, (char *)&dcname); 188 return (NT_STATUS_INTERNAL_ERROR); 189 } 190 191 if (dcname.buf) { 192 (void) strlcpy(namebuf, dcname.buf, namebuflen); 193 194 if ((h = smb_gethostbyname(dcname.buf, &rc)) == NULL) { 195 bzero(ipaddr, sizeof (smb_inaddr_t)); 196 } else { 197 (void) memcpy(ipaddr, h->h_addr, h->h_length); 198 ipaddr->a_family = h->h_addrtype; 199 freehostent(h); 200 } 201 xdr_free(smb_string_xdr, (char *)&dcname); 202 } 203 204 return (NT_STATUS_SUCCESS); 205 } 206 207 bool_t 208 smb_joininfo_xdr(XDR *xdrs, smb_joininfo_t *objp) 209 { 210 if (!xdr_uint32_t(xdrs, &objp->mode)) 211 return (FALSE); 212 213 if (!xdr_vector(xdrs, (char *)objp->domain_name, MAXHOSTNAMELEN, 214 sizeof (char), (xdrproc_t)xdr_char)) 215 return (FALSE); 216 217 if (!xdr_vector(xdrs, (char *)objp->container_name, MAXHOSTNAMELEN, 218 sizeof (char), (xdrproc_t)xdr_char)) 219 return (FALSE); 220 221 if (!xdr_vector(xdrs, (char *)objp->domain_username, 222 SMB_USERNAME_MAXLEN + 1, sizeof (char), (xdrproc_t)xdr_char)) 223 return (FALSE); 224 225 if (!xdr_vector(xdrs, (char *)objp->domain_passwd, 226 SMB_PASSWD_MAXLEN + 1, sizeof (char), (xdrproc_t)xdr_char)) 227 return (FALSE); 228 229 return (TRUE); 230 } 231 232 bool_t 233 smb_joinres_xdr(XDR *xdrs, smb_joinres_t *objp) 234 { 235 236 if (!xdr_uint32_t(xdrs, &objp->status)) 237 return (FALSE); 238 239 if (!xdr_int(xdrs, &objp->join_err)) 240 return (FALSE); 241 242 if (!xdr_vector(xdrs, (char *)objp->dc_name, MAXHOSTNAMELEN, 243 sizeof (char), (xdrproc_t)xdr_char)) 244 return (FALSE); 245 246 return (TRUE); 247 } 248 249 /* 250 * Parameters: 251 * fqdn (input) - fully-qualified domain name 252 * buf (output) - fully-qualified hostname of the AD server found 253 * by this function. 254 * buflen (input) - length of the 'buf' 255 * 256 * Return: 257 * B_TRUE if an AD server is found. Otherwise, returns B_FALSE; 258 * 259 * The buffer passed in should be big enough to hold a fully-qualified 260 * hostname (MAXHOSTNAMELEN); otherwise, a truncated string will be 261 * returned. On error, an empty string will be returned. 262 */ 263 boolean_t 264 smb_find_ads_server(char *fqdn, char *buf, int buflen) 265 { 266 smb_string_t server; 267 smb_string_t domain; 268 boolean_t found = B_FALSE; 269 int rc; 270 271 if (fqdn == NULL || buf == NULL) { 272 if (buf) 273 *buf = '\0'; 274 return (B_FALSE); 275 } 276 277 bzero(&server, sizeof (smb_string_t)); 278 *buf = '\0'; 279 280 domain.buf = fqdn; 281 282 rc = smb_door_call(SMB_DR_ADS_FIND_HOST, &domain, smb_string_xdr, 283 &server, smb_string_xdr); 284 285 if (rc != 0) 286 syslog(LOG_DEBUG, "smb_find_ads_server: %m"); 287 288 if (server.buf != NULL) { 289 if (*server.buf != '\0') { 290 (void) strlcpy(buf, server.buf, buflen); 291 found = B_TRUE; 292 } 293 294 xdr_free(smb_string_xdr, (char *)&server); 295 } 296 297 return (found); 298 } 299 300 void 301 smb_notify_dc_changed(void) 302 { 303 int rc; 304 305 rc = smb_door_call(SMB_DR_NOTIFY_DC_CHANGED, 306 NULL, NULL, NULL, NULL); 307 308 if (rc != 0) 309 syslog(LOG_DEBUG, "smb_notify_dc_changed: %m"); 310 } 311 312 313 /* 314 * After a successful door call the local door_arg->data_ptr is assigned 315 * to the caller's arg->rbuf so that arg has references to both input and 316 * response buffers, which is required by smb_door_free. 317 * 318 * On success, the object referenced by rsp_data will have been populated 319 * by passing rbuf through the rsp_xdr function. 320 */ 321 static int 322 smb_door_call(uint32_t cmd, void *req_data, xdrproc_t req_xdr, 323 void *rsp_data, xdrproc_t rsp_xdr) 324 { 325 smb_doorarg_t da; 326 int fd; 327 int rc; 328 char *door_name; 329 330 bzero(&da, sizeof (smb_doorarg_t)); 331 da.da_opcode = cmd; 332 da.da_opname = smb_doorhdr_opname(cmd); 333 da.da_req_xdr = req_xdr; 334 da.da_rsp_xdr = rsp_xdr; 335 da.da_req_data = req_data; 336 da.da_rsp_data = rsp_data; 337 338 if ((req_data == NULL && req_xdr != NULL) || 339 (rsp_data == NULL && rsp_xdr != NULL)) { 340 errno = EINVAL; 341 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname); 342 return (-1); 343 } 344 345 door_name = getenv("SMBD_DOOR_NAME"); 346 if (door_name == NULL) 347 door_name = SMBD_DOOR_NAME; 348 349 if ((fd = open(door_name, O_RDONLY)) < 0) { 350 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname); 351 return (-1); 352 } 353 354 if (smb_door_encode(&da, cmd) != 0) { 355 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname); 356 (void) close(fd); 357 return (-1); 358 } 359 360 if (smb_door_call_private(fd, &da) != 0) { 361 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname); 362 smb_door_free(&da.da_arg); 363 (void) close(fd); 364 return (-1); 365 } 366 367 if ((rc = smb_door_decode(&da)) != 0) 368 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname); 369 smb_door_free(&da.da_arg); 370 (void) close(fd); 371 return (rc); 372 } 373 374 /* 375 * We use a copy of the door arg because doorfs may change data_ptr 376 * and we want to detect that when freeing the door buffers. After 377 * this call, response data must be referenced via rbuf and rsize. 378 */ 379 static int 380 smb_door_call_private(int fd, smb_doorarg_t *da) 381 { 382 door_arg_t door_arg; 383 int rc; 384 int i; 385 386 bcopy(&da->da_arg, &door_arg, sizeof (door_arg_t)); 387 388 for (i = 0; i < SMB_DOOR_CALL_RETRIES; ++i) { 389 errno = 0; 390 391 if ((rc = door_call(fd, &door_arg)) == 0) 392 break; 393 394 if (errno != EAGAIN && errno != EINTR) 395 return (-1); 396 } 397 398 if (rc != 0 || door_arg.data_size == 0 || door_arg.rsize == 0) { 399 if (errno == 0) 400 errno = EIO; 401 return (-1); 402 } 403 404 da->da_arg.rbuf = door_arg.data_ptr; 405 da->da_arg.rsize = door_arg.rsize; 406 return (rc); 407 } 408 409 static int 410 smb_door_encode(smb_doorarg_t *da, uint32_t cmd) 411 { 412 XDR xdrs; 413 char *buf; 414 uint32_t buflen; 415 416 buflen = xdr_sizeof(smb_doorhdr_xdr, &da->da_hdr); 417 if (da->da_req_xdr != NULL) 418 buflen += xdr_sizeof(da->da_req_xdr, da->da_req_data); 419 420 smb_door_sethdr(&da->da_hdr, cmd, buflen); 421 422 if ((buf = malloc(buflen)) == NULL) 423 return (-1); 424 425 xdrmem_create(&xdrs, buf, buflen, XDR_ENCODE); 426 427 if (!smb_doorhdr_xdr(&xdrs, &da->da_hdr)) { 428 errno = EPROTO; 429 free(buf); 430 xdr_destroy(&xdrs); 431 return (-1); 432 } 433 434 if (da->da_req_xdr != NULL) { 435 if (!da->da_req_xdr(&xdrs, da->da_req_data)) { 436 errno = EPROTO; 437 free(buf); 438 xdr_destroy(&xdrs); 439 return (-1); 440 } 441 } 442 443 da->da_arg.data_ptr = buf; 444 da->da_arg.data_size = buflen; 445 da->da_arg.desc_ptr = NULL; 446 da->da_arg.desc_num = 0; 447 da->da_arg.rbuf = buf; 448 da->da_arg.rsize = buflen; 449 450 xdr_destroy(&xdrs); 451 return (0); 452 } 453 454 /* 455 * Decode the response in rbuf and rsize. 456 */ 457 static int 458 smb_door_decode(smb_doorarg_t *da) 459 { 460 XDR xdrs; 461 smb_doorhdr_t hdr; 462 char *rbuf = da->da_arg.rbuf; 463 uint32_t rsize = da->da_arg.rsize; 464 465 if (rbuf == NULL || rsize == 0) { 466 errno = EINVAL; 467 return (-1); 468 } 469 470 xdrmem_create(&xdrs, rbuf, rsize, XDR_DECODE); 471 472 if (!smb_doorhdr_xdr(&xdrs, &hdr)) { 473 errno = EPROTO; 474 xdr_destroy(&xdrs); 475 return (-1); 476 } 477 478 if (!smb_door_chkhdr(da, &hdr)) { 479 errno = EPROTO; 480 xdr_destroy(&xdrs); 481 return (-1); 482 } 483 484 if (da->da_rsp_xdr != NULL) { 485 if (!da->da_rsp_xdr(&xdrs, da->da_rsp_data)) { 486 errno = EPROTO; 487 xdr_destroy(&xdrs); 488 return (-1); 489 } 490 } 491 492 xdr_destroy(&xdrs); 493 return (0); 494 } 495 496 static void 497 smb_door_sethdr(smb_doorhdr_t *hdr, uint32_t cmd, uint32_t datalen) 498 { 499 bzero(hdr, sizeof (smb_doorhdr_t)); 500 hdr->dh_magic = SMB_DOOR_HDR_MAGIC; 501 hdr->dh_flags = SMB_DF_USERSPACE; 502 hdr->dh_op = cmd; 503 hdr->dh_txid = smb_get_txid(); 504 hdr->dh_datalen = datalen; 505 hdr->dh_door_rc = SMB_DOP_NOT_CALLED; 506 } 507 508 static boolean_t 509 smb_door_chkhdr(smb_doorarg_t *da, smb_doorhdr_t *hdr) 510 { 511 if ((hdr->dh_magic != SMB_DOOR_HDR_MAGIC) || 512 (hdr->dh_op != da->da_hdr.dh_op) || 513 (hdr->dh_txid != da->da_hdr.dh_txid)) { 514 syslog(LOG_DEBUG, "smb_door_chkhdr[%s]: invalid header", 515 da->da_opname); 516 return (B_FALSE); 517 } 518 519 if (hdr->dh_door_rc != SMB_DOP_SUCCESS) { 520 syslog(LOG_DEBUG, "smb_door_chkhdr[%s]: call status=%d", 521 da->da_opname, hdr->dh_door_rc); 522 return (B_FALSE); 523 } 524 525 return (B_TRUE); 526 } 527 528 /* 529 * Free resources allocated for a door call. If the result buffer provided 530 * by the client is too small, doorfs will have allocated a new buffer, 531 * which must be unmapped here. 532 * 533 * This function must be called to free both the argument and result door 534 * buffers regardless of the status of the door call. 535 */ 536 static void 537 smb_door_free(door_arg_t *arg) 538 { 539 if (arg->rbuf && (arg->rbuf != arg->data_ptr)) 540 (void) munmap(arg->rbuf, arg->rsize); 541 542 free(arg->data_ptr); 543 } 544