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