1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
14  */
15 
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <string.h>
21 #include <syslog.h>
22 #include <rpc/rpc.h>
23 #include <sys/uuid.h>
24 #include <smb/ntstatus.h>
25 #include <synch.h>
26 #include <thread.h>
27 #include <arpa/inet.h>
28 #include <uuid/uuid.h>
29 
30 #include "idmapd.h"
31 #include "libadutils.h"
32 #include "dsgetdc.h"
33 #include "ads_priv.h"
34 
35 void adspriv_program_1(struct svc_req *, register SVCXPRT *);
36 
37 SVCXPRT *dcl_xprt = NULL;
38 
39 void
40 init_dc_locator(void)
41 {
42 	int	connmaxrec = 32 * 1024;
43 
44 	dcl_xprt = svc_door_create(adspriv_program_1,
45 	    ADSPRIV_PROGRAM, ADSPRIV_V1, connmaxrec);
46 	if (dcl_xprt == NULL) {
47 		syslog(LOG_ERR, "unable to create door RPC service");
48 		return;
49 	}
50 
51 	if (!svc_control(dcl_xprt, SVCSET_CONNMAXREC, &connmaxrec)) {
52 		syslog(LOG_ERR, "unable to limit RPC request size");
53 	}
54 }
55 
56 void
57 fini_dc_locator(void)
58 {
59 	if (dcl_xprt != NULL)
60 		svc_destroy(dcl_xprt);
61 }
62 
63 /*
64  * Functions called by the (generated) adspriv_srv.c
65  */
66 
67 /* ARGSUSED */
68 bool_t
69 adspriv_null_1_svc(void *result, struct svc_req *rqstp)
70 {
71 	return (TRUE);
72 }
73 
74 /* ARGSUSED */
75 bool_t
76 adspriv_forcerediscovery_1_svc(
77 	DsForceRediscoveryArgs args,
78 	int *res,
79 	struct svc_req *sreq)
80 {
81 	/* Ignoring args for now. */
82 
83 	idmap_cfg_force_rediscovery();
84 	*res = 0;
85 
86 	return (TRUE);
87 }
88 
89 
90 /* ARGSUSED */
91 bool_t
92 adspriv_getdcname_1_svc(
93 	DsGetDcNameArgs args,
94 	DsGetDcNameRes *res,
95 	struct svc_req *sreq)
96 {
97 	uuid_t uuid;
98 	adspriv_dcinfo *dci;
99 	idmap_pg_config_t *pgcfg;
100 	ad_disc_ds_t *ds;
101 	char *s;
102 
103 	/* Init */
104 	(void) memset(res, 0, sizeof (*res));
105 	res->status = 0;
106 	dci = &res->DsGetDcNameRes_u.res0;
107 
108 	if (args.Flags & DS_FORCE_REDISCOVERY)
109 		idmap_cfg_force_rediscovery();
110 
111 	/*
112 	 * We normally should wait if discovery is running.
113 	 * Sort of mis-using the background flag as a way to
114 	 * skip the wait, until we really do background disc.
115 	 */
116 	if ((args.Flags & DS_BACKGROUND_ONLY) == 0) {
117 		timespec_t tv = { 15, 0 };
118 		int rc = 0;
119 		int waited = 0;
120 
121 		(void) mutex_lock(&_idmapdstate.addisc_lk);
122 
123 		if (_idmapdstate.addisc_st != 0)
124 			idmapdlog(LOG_DEBUG, "getdcname wait begin");
125 
126 		while (_idmapdstate.addisc_st != 0) {
127 			waited++;
128 			rc = cond_reltimedwait(&_idmapdstate.addisc_cv,
129 			    &_idmapdstate.addisc_lk, &tv);
130 			if (rc == ETIME)
131 				break;
132 		}
133 		(void) mutex_unlock(&_idmapdstate.addisc_lk);
134 
135 		if (rc == ETIME) {
136 			/* Caller will replace this with DC not found. */
137 			idmapdlog(LOG_ERR, "getdcname timeout");
138 			res->status = NT_STATUS_CANT_WAIT;
139 			return (TRUE);
140 		}
141 		if (waited) {
142 			idmapdlog(LOG_DEBUG, "getdcname wait done");
143 		}
144 	}
145 
146 	RDLOCK_CONFIG();
147 	pgcfg = &_idmapdstate.cfg->pgcfg;
148 
149 	if (pgcfg->domain_name == NULL) {
150 		res->status = NT_STATUS_INVALID_SERVER_STATE;
151 		goto out;
152 	}
153 
154 	if (args.DomainName != NULL && args.DomainName[0] != '\0' &&
155 	    0 != strcasecmp(args.DomainName, pgcfg->domain_name)) {
156 		/*
157 		 * They asked for a specific domain not our primary,
158 		 * which is not supported (and not needed).
159 		 */
160 		res->status = NT_STATUS_NO_SUCH_DOMAIN;
161 		goto out;
162 	}
163 
164 	if ((ds = pgcfg->domain_controller) == NULL) {
165 		res->status = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
166 		goto out;
167 	}
168 
169 	dci->dci_DcName = strdup(ds->host);
170 
171 	dci->dci_DcAddr = calloc(1, INET6_ADDRSTRLEN);
172 	if (dci->dci_DcAddr != NULL &&
173 	    ad_disc_getnameinfo(dci->dci_DcAddr, INET6_ADDRSTRLEN,
174 	    &ds->addr) == 0)
175 		dci->dci_AddrType = DS_INET_ADDRESS;
176 
177 	if ((s = pgcfg->domain_guid) != NULL &&
178 	    0 == uuid_parse(s, uuid)) {
179 		(void) memcpy(dci->dci_guid, uuid, sizeof (uuid));
180 	}
181 
182 	if ((s = pgcfg->domain_name) != NULL)
183 		dci->dci_DomainName = strdup(s);
184 
185 	if ((s = pgcfg->forest_name) != NULL)
186 		dci->dci_DnsForestName = strdup(s);
187 
188 	dci->dci_Flags = ds->flags;
189 	dci->dci_DcSiteName = strdup(ds->site);
190 
191 	if ((s = pgcfg->site_name) != NULL)
192 		dci->dci_ClientSiteName = strdup(s);
193 
194 	/* Address in binary form too. */
195 	(void) memcpy(&dci->dci_sockaddr,
196 	    &ds->addr, ADSPRIV_SOCKADDR_LEN);
197 
198 out:
199 	UNLOCK_CONFIG();
200 
201 	return (TRUE);
202 }
203 
204 /* ARGSUSED */
205 int
206 adspriv_program_1_freeresult(SVCXPRT *xprt, xdrproc_t fun, caddr_t res)
207 {
208 	(void) xdr_free(fun, res);
209 	return (TRUE);
210 }
211