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