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 2015 Joyent, Inc.
14  */
15 
16 /*
17  * varpd door server logic
18  */
19 
20 #include <door.h>
21 #include <errno.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <unistd.h>
26 #include <stropts.h>
27 #include <stdlib.h>
28 #include <strings.h>
29 #include <priv.h>
30 #include <libvarpd_impl.h>
31 
32 typedef int (libvarpd_door_f)(varpd_impl_t *, varpd_client_arg_t *, ucred_t *);
33 
34 static boolean_t
libvarpd_door_privileged(ucred_t * credp)35 libvarpd_door_privileged(ucred_t *credp)
36 {
37 	const priv_set_t *ps;
38 
39 	ps = ucred_getprivset(credp, PRIV_EFFECTIVE);
40 	if (ps == NULL)
41 		return (B_FALSE);
42 
43 	return (priv_ismember(ps, PRIV_SYS_NET_CONFIG));
44 }
45 
46 /* ARGSUSED */
47 static int
libvarpd_door_f_create(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)48 libvarpd_door_f_create(varpd_impl_t *vip, varpd_client_arg_t *vcap,
49     ucred_t *credp)
50 {
51 	int ret;
52 	varpd_instance_handle_t *ihdl;
53 	varpd_client_create_arg_t *vccap = &vcap->vca_un.vca_create;
54 
55 	vccap->vcca_plugin[LIBVARPD_PROP_NAMELEN-1] = '\0';
56 	ret = libvarpd_instance_create((varpd_handle_t *)vip,
57 	    vccap->vcca_linkid, vccap->vcca_plugin, &ihdl);
58 	if (ret == 0)
59 		vccap->vcca_id = libvarpd_instance_id(ihdl);
60 
61 	return (ret);
62 }
63 
64 /* ARGSUSED */
65 static int
libvarpd_door_f_activate(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)66 libvarpd_door_f_activate(varpd_impl_t *vip, varpd_client_arg_t *vcap,
67     ucred_t *credp)
68 {
69 	varpd_instance_handle_t *ihp;
70 	varpd_client_instance_arg_t *vciap = &vcap->vca_un.vca_instance;
71 
72 	ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vciap->vcia_id);
73 	if (ihp == NULL)
74 		return (ENOENT);
75 	return (libvarpd_instance_activate(ihp));
76 }
77 
78 /* ARGSUSED */
79 static int
libvarpd_door_f_destroy(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)80 libvarpd_door_f_destroy(varpd_impl_t *vip, varpd_client_arg_t *vcap,
81     ucred_t *credp)
82 {
83 	varpd_instance_handle_t *ihp;
84 	varpd_client_instance_arg_t *vciap = &vcap->vca_un.vca_instance;
85 
86 	ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vciap->vcia_id);
87 	if (ihp == NULL)
88 		return (ENOENT);
89 	libvarpd_instance_destroy(ihp);
90 	return (0);
91 }
92 
93 /* ARGSUSED */
94 static int
libvarpd_door_f_nprops(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)95 libvarpd_door_f_nprops(varpd_impl_t *vip, varpd_client_arg_t *vcap,
96     ucred_t *credp)
97 {
98 	varpd_instance_handle_t *ihp;
99 	varpd_client_nprops_arg_t *vcnap = &vcap->vca_un.vca_nprops;
100 
101 	ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vcnap->vcna_id);
102 	if (ihp == NULL)
103 		return (ENOENT);
104 
105 	return (libvarpd_prop_nprops(ihp, &vcnap->vcna_nprops));
106 }
107 
108 /* ARGSUSED */
109 static int
libvarpd_door_f_propinfo(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)110 libvarpd_door_f_propinfo(varpd_impl_t *vip, varpd_client_arg_t *vcap,
111     ucred_t *credp)
112 {
113 	int ret;
114 	varpd_instance_handle_t *ihp;
115 	varpd_prop_handle_t *phdl;
116 	varpd_client_propinfo_arg_t *vcfap = &vcap->vca_un.vca_info;
117 
118 	ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vcfap->vcfa_id);
119 	if (ihp == NULL)
120 		return (ENOENT);
121 	ret = libvarpd_prop_handle_alloc((varpd_handle_t *)vip, ihp, &phdl);
122 	if (ret != 0)
123 		return (ret);
124 
125 	if (vcfap->vcfa_propid != UINT_MAX) {
126 		ret = libvarpd_prop_info_fill(phdl, vcfap->vcfa_propid);
127 		if (ret != 0) {
128 			libvarpd_prop_handle_free(phdl);
129 			return (ret);
130 		}
131 	} else {
132 		uint_t i, nprop;
133 		const char *name;
134 
135 		vcfap->vcfa_name[LIBVARPD_PROP_NAMELEN-1] = '\0';
136 		ret = libvarpd_prop_nprops(ihp, &nprop);
137 		if (ret != 0) {
138 			libvarpd_prop_handle_free(phdl);
139 			return (ret);
140 		}
141 		for (i = 0; i < nprop; i++) {
142 			ret = libvarpd_prop_info_fill(phdl, i);
143 			if (ret != 0) {
144 				libvarpd_prop_handle_free(phdl);
145 				return (ret);
146 			}
147 			ret = libvarpd_prop_info(phdl, &name, NULL, NULL, NULL,
148 			    NULL, NULL);
149 			if (ret != 0) {
150 				libvarpd_prop_handle_free(phdl);
151 				return (ret);
152 			}
153 			if (strcmp(vcfap->vcfa_name, name) == 0)
154 				break;
155 		}
156 
157 		if (i == nprop) {
158 			libvarpd_prop_handle_free(phdl);
159 			return (ENOENT);
160 		}
161 		vcfap->vcfa_propid = i;
162 	}
163 	libvarpd_prop_door_convert(phdl, vcfap);
164 	libvarpd_prop_handle_free(phdl);
165 	return (0);
166 }
167 
168 /* ARGSUSED */
169 static int
libvarpd_door_f_getprop(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)170 libvarpd_door_f_getprop(varpd_impl_t *vip, varpd_client_arg_t *vcap,
171     ucred_t *credp)
172 {
173 	int ret;
174 	uint32_t size;
175 	varpd_instance_handle_t *ihp;
176 	varpd_prop_handle_t *phdl;
177 	varpd_client_prop_arg_t *vcpap = &vcap->vca_un.vca_prop;
178 
179 	ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vcpap->vcpa_id);
180 	if (ihp == NULL)
181 		return (ENOENT);
182 	ret = libvarpd_prop_handle_alloc((varpd_handle_t *)vip, ihp, &phdl);
183 	if (ret != 0)
184 		return (ret);
185 
186 	ret = libvarpd_prop_info_fill(phdl, vcpap->vcpa_propid);
187 	if (ret != 0) {
188 		libvarpd_prop_handle_free(phdl);
189 		return (ret);
190 	}
191 
192 	ret = libvarpd_prop_get(phdl, vcpap->vcpa_buf, &size);
193 	if (ret == 0)
194 		vcpap->vcpa_bufsize = size;
195 	libvarpd_prop_handle_free(phdl);
196 	return (0);
197 }
198 
199 /* ARGSUSED */
200 static int
libvarpd_door_f_setprop(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)201 libvarpd_door_f_setprop(varpd_impl_t *vip, varpd_client_arg_t *vcap,
202     ucred_t *credp)
203 {
204 	int ret;
205 	varpd_instance_handle_t *ihp;
206 	varpd_prop_handle_t *phdl;
207 	varpd_client_prop_arg_t *vcpap = &vcap->vca_un.vca_prop;
208 
209 	ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vcpap->vcpa_id);
210 	if (ihp == NULL)
211 		return (ENOENT);
212 	ret = libvarpd_prop_handle_alloc((varpd_handle_t *)vip, ihp, &phdl);
213 	if (ret != 0)
214 		return (ret);
215 
216 	ret = libvarpd_prop_info_fill(phdl, vcpap->vcpa_propid);
217 	if (ret != 0) {
218 		libvarpd_prop_handle_free(phdl);
219 		return (ret);
220 	}
221 
222 	ret = libvarpd_prop_set(phdl, vcpap->vcpa_buf, vcpap->vcpa_bufsize);
223 	libvarpd_prop_handle_free(phdl);
224 	return (ret);
225 }
226 
227 /* ARGSUSED */
228 static int
libvarpd_door_f_lookup(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)229 libvarpd_door_f_lookup(varpd_impl_t *vip, varpd_client_arg_t *vcap,
230     ucred_t *credp)
231 {
232 	varpd_instance_t *inst;
233 	varpd_client_lookup_arg_t *vclap = &vcap->vca_un.vca_lookup;
234 
235 	inst = libvarpd_instance_lookup_by_dlid(vip, vclap->vcla_linkid);
236 	if (inst == NULL)
237 		return (ENOENT);
238 
239 	vclap->vcla_id = inst->vri_id;
240 	return (0);
241 }
242 
243 /* ARGSUSED */
244 static int
libvarpd_door_f_target(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)245 libvarpd_door_f_target(varpd_impl_t *vip, varpd_client_arg_t *vcap,
246     ucred_t *credp)
247 {
248 	varpd_instance_handle_t *ihp;
249 	varpd_instance_t *inst;
250 	varpd_client_target_mode_arg_t *vtmap = &vcap->vca_un.vca_mode;
251 
252 	ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vtmap->vtma_id);
253 	if (ihp == NULL)
254 		return (ENOENT);
255 	inst = (varpd_instance_t *)ihp;
256 	vtmap->vtma_dest = inst->vri_dest;
257 	vtmap->vtma_mode = inst->vri_mode;
258 	return (0);
259 }
260 
261 static int
libvarpd_door_f_flush(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)262 libvarpd_door_f_flush(varpd_impl_t *vip, varpd_client_arg_t *vcap,
263     ucred_t *credp)
264 {
265 	varpd_instance_handle_t *ihp;
266 	varpd_client_target_cache_arg_t *vtcap = &vcap->vca_un.vca_cache;
267 
268 	if (libvarpd_door_privileged(credp) == B_FALSE)
269 		return (EPERM);
270 
271 	ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vtcap->vtca_id);
272 	if (ihp == NULL)
273 		return (ENOENT);
274 	return (libvarpd_overlay_cache_flush((varpd_instance_t *)ihp));
275 }
276 
277 static int
libvarpd_door_f_delete(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)278 libvarpd_door_f_delete(varpd_impl_t *vip, varpd_client_arg_t *vcap,
279     ucred_t *credp)
280 {
281 	varpd_instance_handle_t *ihp;
282 	varpd_client_target_cache_arg_t *vtcap = &vcap->vca_un.vca_cache;
283 
284 	if (libvarpd_door_privileged(credp) == B_FALSE)
285 		return (EPERM);
286 
287 	ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vtcap->vtca_id);
288 	if (ihp == NULL)
289 		return (ENOENT);
290 	return (libvarpd_overlay_cache_delete((varpd_instance_t *)ihp,
291 	    vtcap->vtca_key));
292 }
293 
294 /* ARGSUSED */
295 static int
libvarpd_door_f_get(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)296 libvarpd_door_f_get(varpd_impl_t *vip, varpd_client_arg_t *vcap,
297     ucred_t *credp)
298 {
299 	varpd_instance_handle_t *ihp;
300 	varpd_client_target_cache_arg_t *vtcap = &vcap->vca_un.vca_cache;
301 
302 	ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vtcap->vtca_id);
303 	if (ihp == NULL)
304 		return (ENOENT);
305 	return (libvarpd_overlay_cache_get((varpd_instance_t *)ihp,
306 	    vtcap->vtca_key, &vtcap->vtca_entry));
307 }
308 
309 static int
libvarpd_door_f_set(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)310 libvarpd_door_f_set(varpd_impl_t *vip, varpd_client_arg_t *vcap,
311     ucred_t *credp)
312 {
313 	varpd_instance_handle_t *ihp;
314 	varpd_client_target_cache_arg_t *vtcap = &vcap->vca_un.vca_cache;
315 
316 	if (libvarpd_door_privileged(credp) == B_FALSE)
317 		return (EPERM);
318 
319 	ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vtcap->vtca_id);
320 	if (ihp == NULL)
321 		return (ENOENT);
322 
323 	return (libvarpd_overlay_cache_set((varpd_instance_t *)ihp,
324 	    vtcap->vtca_key, &vtcap->vtca_entry));
325 }
326 
327 /* ARGSUSED */
328 static int
libvarpd_door_f_walk(varpd_impl_t * vip,varpd_client_arg_t * vcap,ucred_t * credp)329 libvarpd_door_f_walk(varpd_impl_t *vip, varpd_client_arg_t *vcap,
330     ucred_t *credp)
331 {
332 	varpd_instance_handle_t *ihp;
333 	varpd_client_target_walk_arg_t *vctwp = &vcap->vca_un.vca_walk;
334 
335 	ihp = libvarpd_instance_lookup((varpd_handle_t *)vip, vctwp->vtcw_id);
336 	if (ihp == NULL)
337 		return (ENOENT);
338 
339 	return (libvarpd_overlay_cache_walk_fill((varpd_instance_t *)ihp,
340 	    &vctwp->vtcw_marker, &vctwp->vtcw_count, vctwp->vtcw_ents));
341 }
342 
343 static libvarpd_door_f *libvarpd_door_table[] = {
344 	libvarpd_door_f_create,
345 	libvarpd_door_f_activate,
346 	libvarpd_door_f_destroy,
347 	libvarpd_door_f_nprops,
348 	libvarpd_door_f_propinfo,
349 	libvarpd_door_f_getprop,
350 	libvarpd_door_f_setprop,
351 	libvarpd_door_f_lookup,
352 	libvarpd_door_f_target,
353 	libvarpd_door_f_flush,
354 	libvarpd_door_f_delete,
355 	libvarpd_door_f_get,
356 	libvarpd_door_f_set,
357 	libvarpd_door_f_walk
358 };
359 
360 /* ARGSUSED */
361 static void
libvarpd_door_server(void * cookie,char * argp,size_t argsz,door_desc_t * dp,uint_t ndesc)362 libvarpd_door_server(void *cookie, char *argp, size_t argsz, door_desc_t *dp,
363     uint_t ndesc)
364 {
365 	int ret;
366 	varpd_client_eresp_t err;
367 	ucred_t *credp = NULL;
368 	varpd_impl_t *vip = cookie;
369 	varpd_client_arg_t *vcap = (varpd_client_arg_t *)argp;
370 
371 	err.vce_command = VARPD_CLIENT_INVALID;
372 	if (argsz < sizeof (varpd_client_arg_t)) {
373 		err.vce_errno = EINVAL;
374 		goto errout;
375 	}
376 
377 	if ((ret = door_ucred(&credp)) != 0) {
378 		err.vce_errno = ret;
379 		goto errout;
380 	}
381 
382 	if (vcap->vca_command == VARPD_CLIENT_INVALID ||
383 	    vcap->vca_command >= VARPD_CLIENT_MAX) {
384 		err.vce_errno = EINVAL;
385 		goto errout;
386 	}
387 
388 	vcap->vca_errno = 0;
389 	ret = libvarpd_door_table[vcap->vca_command - 1](vip, vcap, credp);
390 	if (ret != 0)
391 		vcap->vca_errno = ret;
392 
393 	ucred_free(credp);
394 	(void) door_return(argp, argsz, NULL, 0);
395 	return;
396 
397 errout:
398 	ucred_free(credp);
399 	(void) door_return((char *)&err, sizeof (err), NULL, 0);
400 }
401 
402 int
libvarpd_door_server_create(varpd_handle_t * vhp,const char * path)403 libvarpd_door_server_create(varpd_handle_t *vhp, const char *path)
404 {
405 	int fd, ret;
406 	varpd_impl_t *vip = (varpd_impl_t *)vhp;
407 
408 	mutex_enter(&vip->vdi_lock);
409 	if (vip->vdi_doorfd >= 0) {
410 		mutex_exit(&vip->vdi_lock);
411 		return (EEXIST);
412 	}
413 
414 	vip->vdi_doorfd = door_create(libvarpd_door_server, vip,
415 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
416 	if (vip->vdi_doorfd == -1) {
417 		mutex_exit(&vip->vdi_lock);
418 		return (errno);
419 	}
420 
421 	if ((fd = open(path, O_CREAT | O_RDWR, 0666)) == -1) {
422 		ret = errno;
423 		if (door_revoke(vip->vdi_doorfd) != 0)
424 			libvarpd_panic("failed to revoke door: %d",
425 			    errno);
426 		mutex_exit(&vip->vdi_lock);
427 		return (errno);
428 	}
429 
430 	if (fchown(fd, UID_NETADM, GID_NETADM) != 0) {
431 		ret = errno;
432 		if (door_revoke(vip->vdi_doorfd) != 0)
433 			libvarpd_panic("failed to revoke door: %d",
434 			    errno);
435 		mutex_exit(&vip->vdi_lock);
436 		return (ret);
437 	}
438 
439 	if (close(fd) != 0)
440 		libvarpd_panic("failed to close door fd %d: %d",
441 		    fd, errno);
442 	(void) fdetach(path);
443 	if (fattach(vip->vdi_doorfd, path) != 0) {
444 		ret = errno;
445 		if (door_revoke(vip->vdi_doorfd) != 0)
446 			libvarpd_panic("failed to revoke door: %d",
447 			    errno);
448 		mutex_exit(&vip->vdi_lock);
449 		return (ret);
450 	}
451 
452 	mutex_exit(&vip->vdi_lock);
453 	return (0);
454 }
455 
456 void
libvarpd_door_server_destroy(varpd_handle_t * vhp)457 libvarpd_door_server_destroy(varpd_handle_t *vhp)
458 {
459 	varpd_impl_t *vip = (varpd_impl_t *)vhp;
460 
461 	mutex_enter(&vip->vdi_lock);
462 	if (vip->vdi_doorfd != 0) {
463 		if (door_revoke(vip->vdi_doorfd) != 0)
464 			libvarpd_panic("failed to revoke door: %d",
465 			    errno);
466 		vip->vdi_doorfd = -1;
467 	}
468 	mutex_exit(&vip->vdi_lock);
469 }
470