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