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_vector(xdrs, (char *)objp->domain_name, MAXHOSTNAMELEN, 211 sizeof (char), (xdrproc_t)xdr_char)) 212 return (FALSE); 213 214 if (!xdr_vector(xdrs, (char *)objp->domain_username, 215 SMB_USERNAME_MAXLEN + 1, sizeof (char), (xdrproc_t)xdr_char)) 216 return (FALSE); 217 218 if (!xdr_vector(xdrs, (char *)objp->domain_passwd, 219 SMB_PASSWD_MAXLEN + 1, sizeof (char), (xdrproc_t)xdr_char)) 220 return (FALSE); 221 222 if (!xdr_uint32_t(xdrs, &objp->mode)) 223 return (FALSE); 224 225 return (TRUE); 226 } 227 228 bool_t 229 smb_joinres_xdr(XDR *xdrs, smb_joinres_t *objp) 230 { 231 232 if (!xdr_uint32_t(xdrs, &objp->status)) 233 return (FALSE); 234 235 if (!xdr_int(xdrs, &objp->join_err)) 236 return (FALSE); 237 238 if (!xdr_vector(xdrs, (char *)objp->dc_name, MAXHOSTNAMELEN, 239 sizeof (char), (xdrproc_t)xdr_char)) 240 return (FALSE); 241 242 return (TRUE); 243 } 244 245 /* 246 * Parameters: 247 * fqdn (input) - fully-qualified domain name 248 * buf (output) - fully-qualified hostname of the AD server found 249 * by this function. 250 * buflen (input) - length of the 'buf' 251 * 252 * Return: 253 * B_TRUE if an AD server is found. Otherwise, returns B_FALSE; 254 * 255 * The buffer passed in should be big enough to hold a fully-qualified 256 * hostname (MAXHOSTNAMELEN); otherwise, a truncated string will be 257 * returned. On error, an empty string will be returned. 258 */ 259 boolean_t 260 smb_find_ads_server(char *fqdn, char *buf, int buflen) 261 { 262 smb_string_t server; 263 smb_string_t domain; 264 boolean_t found = B_FALSE; 265 int rc; 266 267 if (fqdn == NULL || buf == NULL) { 268 if (buf) 269 *buf = '\0'; 270 return (B_FALSE); 271 } 272 273 bzero(&server, sizeof (smb_string_t)); 274 *buf = '\0'; 275 276 domain.buf = fqdn; 277 278 rc = smb_door_call(SMB_DR_ADS_FIND_HOST, &domain, smb_string_xdr, 279 &server, smb_string_xdr); 280 281 if (rc != 0) 282 syslog(LOG_DEBUG, "smb_find_ads_server: %m"); 283 284 if (server.buf != NULL) { 285 if (*server.buf != '\0') { 286 (void) strlcpy(buf, server.buf, buflen); 287 found = B_TRUE; 288 } 289 290 xdr_free(smb_string_xdr, (char *)&server); 291 } 292 293 return (found); 294 } 295 296 void 297 smb_notify_dc_changed(void) 298 { 299 int rc; 300 301 rc = smb_door_call(SMB_DR_NOTIFY_DC_CHANGED, 302 NULL, NULL, NULL, NULL); 303 304 if (rc != 0) 305 syslog(LOG_DEBUG, "smb_notify_dc_changed: %m"); 306 } 307 308 309 /* 310 * After a successful door call the local door_arg->data_ptr is assigned 311 * to the caller's arg->rbuf so that arg has references to both input and 312 * response buffers, which is required by smb_door_free. 313 * 314 * On success, the object referenced by rsp_data will have been populated 315 * by passing rbuf through the rsp_xdr function. 316 */ 317 static int 318 smb_door_call(uint32_t cmd, void *req_data, xdrproc_t req_xdr, 319 void *rsp_data, xdrproc_t rsp_xdr) 320 { 321 smb_doorarg_t da; 322 int fd; 323 int rc; 324 char *door_name; 325 326 bzero(&da, sizeof (smb_doorarg_t)); 327 da.da_opcode = cmd; 328 da.da_opname = smb_doorhdr_opname(cmd); 329 da.da_req_xdr = req_xdr; 330 da.da_rsp_xdr = rsp_xdr; 331 da.da_req_data = req_data; 332 da.da_rsp_data = rsp_data; 333 334 if ((req_data == NULL && req_xdr != NULL) || 335 (rsp_data == NULL && rsp_xdr != NULL)) { 336 errno = EINVAL; 337 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname); 338 return (-1); 339 } 340 341 door_name = getenv("SMBD_DOOR_NAME"); 342 if (door_name == NULL) 343 door_name = SMBD_DOOR_NAME; 344 345 if ((fd = open(door_name, O_RDONLY)) < 0) { 346 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname); 347 return (-1); 348 } 349 350 if (smb_door_encode(&da, cmd) != 0) { 351 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname); 352 (void) close(fd); 353 return (-1); 354 } 355 356 if (smb_door_call_private(fd, &da) != 0) { 357 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname); 358 smb_door_free(&da.da_arg); 359 (void) close(fd); 360 return (-1); 361 } 362 363 if ((rc = smb_door_decode(&da)) != 0) 364 syslog(LOG_DEBUG, "smb_door_call[%s]: %m", da.da_opname); 365 smb_door_free(&da.da_arg); 366 (void) close(fd); 367 return (rc); 368 } 369 370 /* 371 * We use a copy of the door arg because doorfs may change data_ptr 372 * and we want to detect that when freeing the door buffers. After 373 * this call, response data must be referenced via rbuf and rsize. 374 */ 375 static int 376 smb_door_call_private(int fd, smb_doorarg_t *da) 377 { 378 door_arg_t door_arg; 379 int rc; 380 int i; 381 382 bcopy(&da->da_arg, &door_arg, sizeof (door_arg_t)); 383 384 for (i = 0; i < SMB_DOOR_CALL_RETRIES; ++i) { 385 errno = 0; 386 387 if ((rc = door_call(fd, &door_arg)) == 0) 388 break; 389 390 if (errno != EAGAIN && errno != EINTR) 391 return (-1); 392 } 393 394 if (rc != 0 || door_arg.data_size == 0 || door_arg.rsize == 0) { 395 if (errno == 0) 396 errno = EIO; 397 return (-1); 398 } 399 400 da->da_arg.rbuf = door_arg.data_ptr; 401 da->da_arg.rsize = door_arg.rsize; 402 return (rc); 403 } 404 405 static int 406 smb_door_encode(smb_doorarg_t *da, uint32_t cmd) 407 { 408 XDR xdrs; 409 char *buf; 410 uint32_t buflen; 411 412 buflen = xdr_sizeof(smb_doorhdr_xdr, &da->da_hdr); 413 if (da->da_req_xdr != NULL) 414 buflen += xdr_sizeof(da->da_req_xdr, da->da_req_data); 415 416 smb_door_sethdr(&da->da_hdr, cmd, buflen); 417 418 if ((buf = malloc(buflen)) == NULL) 419 return (-1); 420 421 xdrmem_create(&xdrs, buf, buflen, XDR_ENCODE); 422 423 if (!smb_doorhdr_xdr(&xdrs, &da->da_hdr)) { 424 errno = EPROTO; 425 free(buf); 426 xdr_destroy(&xdrs); 427 return (-1); 428 } 429 430 if (da->da_req_xdr != NULL) { 431 if (!da->da_req_xdr(&xdrs, da->da_req_data)) { 432 errno = EPROTO; 433 free(buf); 434 xdr_destroy(&xdrs); 435 return (-1); 436 } 437 } 438 439 da->da_arg.data_ptr = buf; 440 da->da_arg.data_size = buflen; 441 da->da_arg.desc_ptr = NULL; 442 da->da_arg.desc_num = 0; 443 da->da_arg.rbuf = buf; 444 da->da_arg.rsize = buflen; 445 446 xdr_destroy(&xdrs); 447 return (0); 448 } 449 450 /* 451 * Decode the response in rbuf and rsize. 452 */ 453 static int 454 smb_door_decode(smb_doorarg_t *da) 455 { 456 XDR xdrs; 457 smb_doorhdr_t hdr; 458 char *rbuf = da->da_arg.rbuf; 459 uint32_t rsize = da->da_arg.rsize; 460 461 if (rbuf == NULL || rsize == 0) { 462 errno = EINVAL; 463 return (-1); 464 } 465 466 xdrmem_create(&xdrs, rbuf, rsize, XDR_DECODE); 467 468 if (!smb_doorhdr_xdr(&xdrs, &hdr)) { 469 errno = EPROTO; 470 xdr_destroy(&xdrs); 471 return (-1); 472 } 473 474 if (!smb_door_chkhdr(da, &hdr)) { 475 errno = EPROTO; 476 xdr_destroy(&xdrs); 477 return (-1); 478 } 479 480 if (da->da_rsp_xdr != NULL) { 481 if (!da->da_rsp_xdr(&xdrs, da->da_rsp_data)) { 482 errno = EPROTO; 483 xdr_destroy(&xdrs); 484 return (-1); 485 } 486 } 487 488 xdr_destroy(&xdrs); 489 return (0); 490 } 491 492 static void 493 smb_door_sethdr(smb_doorhdr_t *hdr, uint32_t cmd, uint32_t datalen) 494 { 495 bzero(hdr, sizeof (smb_doorhdr_t)); 496 hdr->dh_magic = SMB_DOOR_HDR_MAGIC; 497 hdr->dh_flags = SMB_DF_USERSPACE; 498 hdr->dh_op = cmd; 499 hdr->dh_txid = smb_get_txid(); 500 hdr->dh_datalen = datalen; 501 hdr->dh_door_rc = SMB_DOP_NOT_CALLED; 502 } 503 504 static boolean_t 505 smb_door_chkhdr(smb_doorarg_t *da, smb_doorhdr_t *hdr) 506 { 507 if ((hdr->dh_magic != SMB_DOOR_HDR_MAGIC) || 508 (hdr->dh_op != da->da_hdr.dh_op) || 509 (hdr->dh_txid != da->da_hdr.dh_txid)) { 510 syslog(LOG_DEBUG, "smb_door_chkhdr[%s]: invalid header", 511 da->da_opname); 512 return (B_FALSE); 513 } 514 515 if (hdr->dh_door_rc != SMB_DOP_SUCCESS) { 516 syslog(LOG_DEBUG, "smb_door_chkhdr[%s]: call status=%d", 517 da->da_opname, hdr->dh_door_rc); 518 return (B_FALSE); 519 } 520 521 return (B_TRUE); 522 } 523 524 /* 525 * Free resources allocated for a door call. If the result buffer provided 526 * by the client is too small, doorfs will have allocated a new buffer, 527 * which must be unmapped here. 528 * 529 * This function must be called to free both the argument and result door 530 * buffers regardless of the status of the door call. 531 */ 532 static void 533 smb_door_free(door_arg_t *arg) 534 { 535 if (arg->rbuf && (arg->rbuf != arg->data_ptr)) 536 (void) munmap(arg->rbuf, arg->rsize); 537 538 free(arg->data_ptr); 539 } 540