xref: /titanic_50/usr/src/cmd/hotplugd/hotplugd_door.c (revision aab83bb83be7342f6cfccaed8d5fe0b2f404855d)
1*26947304SEvan Yan /*
2*26947304SEvan Yan  * CDDL HEADER START
3*26947304SEvan Yan  *
4*26947304SEvan Yan  * The contents of this file are subject to the terms of the
5*26947304SEvan Yan  * Common Development and Distribution License (the "License").
6*26947304SEvan Yan  * You may not use this file except in compliance with the License.
7*26947304SEvan Yan  *
8*26947304SEvan Yan  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*26947304SEvan Yan  * or http://www.opensolaris.org/os/licensing.
10*26947304SEvan Yan  * See the License for the specific language governing permissions
11*26947304SEvan Yan  * and limitations under the License.
12*26947304SEvan Yan  *
13*26947304SEvan Yan  * When distributing Covered Code, include this CDDL HEADER in each
14*26947304SEvan Yan  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*26947304SEvan Yan  * If applicable, add the following below this CDDL HEADER, with the
16*26947304SEvan Yan  * fields enclosed by brackets "[]" replaced with your own identifying
17*26947304SEvan Yan  * information: Portions Copyright [yyyy] [name of copyright owner]
18*26947304SEvan Yan  *
19*26947304SEvan Yan  * CDDL HEADER END
20*26947304SEvan Yan  */
21*26947304SEvan Yan 
22*26947304SEvan Yan /*
23*26947304SEvan Yan  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24*26947304SEvan Yan  * Use is subject to license terms.
25*26947304SEvan Yan  */
26*26947304SEvan Yan 
27*26947304SEvan Yan #include <stdio.h>
28*26947304SEvan Yan #include <stdlib.h>
29*26947304SEvan Yan #include <unistd.h>
30*26947304SEvan Yan #include <fcntl.h>
31*26947304SEvan Yan #include <errno.h>
32*26947304SEvan Yan #include <strings.h>
33*26947304SEvan Yan #include <alloca.h>
34*26947304SEvan Yan #include <door.h>
35*26947304SEvan Yan #include <pthread.h>
36*26947304SEvan Yan #include <synch.h>
37*26947304SEvan Yan #include <pwd.h>
38*26947304SEvan Yan #include <auth_list.h>
39*26947304SEvan Yan #include <auth_attr.h>
40*26947304SEvan Yan #include <bsm/adt.h>
41*26947304SEvan Yan #include <bsm/adt_event.h>
42*26947304SEvan Yan #include <sys/sunddi.h>
43*26947304SEvan Yan #include <sys/ddi_hp.h>
44*26947304SEvan Yan #include <libnvpair.h>
45*26947304SEvan Yan #include <libhotplug.h>
46*26947304SEvan Yan #include <libhotplug_impl.h>
47*26947304SEvan Yan #include "hotplugd_impl.h"
48*26947304SEvan Yan 
49*26947304SEvan Yan /*
50*26947304SEvan Yan  * Buffer management for results.
51*26947304SEvan Yan  */
52*26947304SEvan Yan typedef struct i_buffer {
53*26947304SEvan Yan 	uint64_t	seqnum;
54*26947304SEvan Yan 	char		*buffer;
55*26947304SEvan Yan 	struct i_buffer	*next;
56*26947304SEvan Yan } i_buffer_t;
57*26947304SEvan Yan 
58*26947304SEvan Yan static uint64_t		buffer_seqnum = 1;
59*26947304SEvan Yan static i_buffer_t	*buffer_list = NULL;
60*26947304SEvan Yan static pthread_mutex_t	buffer_lock = PTHREAD_MUTEX_INITIALIZER;
61*26947304SEvan Yan 
62*26947304SEvan Yan /*
63*26947304SEvan Yan  * Door file descriptor.
64*26947304SEvan Yan  */
65*26947304SEvan Yan static int	door_fd = -1;
66*26947304SEvan Yan 
67*26947304SEvan Yan /*
68*26947304SEvan Yan  * Function prototypes.
69*26947304SEvan Yan  */
70*26947304SEvan Yan static void	door_server(void *, char *, size_t, door_desc_t *, uint_t);
71*26947304SEvan Yan static int	check_auth(ucred_t *, const char *);
72*26947304SEvan Yan static int	cmd_getinfo(nvlist_t *, nvlist_t **);
73*26947304SEvan Yan static int	cmd_changestate(nvlist_t *, nvlist_t **);
74*26947304SEvan Yan static int	cmd_private(hp_cmd_t, nvlist_t *, nvlist_t **);
75*26947304SEvan Yan static void	add_buffer(uint64_t, char *);
76*26947304SEvan Yan static void	free_buffer(uint64_t);
77*26947304SEvan Yan static uint64_t	get_seqnum(void);
78*26947304SEvan Yan static char	*state_str(int);
79*26947304SEvan Yan static int	audit_session(ucred_t *, adt_session_data_t **);
80*26947304SEvan Yan static void	audit_changestate(ucred_t *, char *, char *, char *, int, int,
81*26947304SEvan Yan 		    int);
82*26947304SEvan Yan static void	audit_setprivate(ucred_t *, char *, char *, char *, char *,
83*26947304SEvan Yan 		    int);
84*26947304SEvan Yan 
85*26947304SEvan Yan /*
86*26947304SEvan Yan  * door_server_init()
87*26947304SEvan Yan  *
88*26947304SEvan Yan  *	Create the door file, and initialize the door server.
89*26947304SEvan Yan  */
90*26947304SEvan Yan boolean_t
door_server_init(void)91*26947304SEvan Yan door_server_init(void)
92*26947304SEvan Yan {
93*26947304SEvan Yan 	int	fd;
94*26947304SEvan Yan 
95*26947304SEvan Yan 	/* Create the door file */
96*26947304SEvan Yan 	if ((fd = open(HOTPLUGD_DOOR, O_CREAT|O_EXCL|O_RDONLY, 0644)) == -1) {
97*26947304SEvan Yan 		if (errno == EEXIST) {
98*26947304SEvan Yan 			log_err("Door service is already running.\n");
99*26947304SEvan Yan 		} else {
100*26947304SEvan Yan 			log_err("Cannot open door file '%s': %s\n",
101*26947304SEvan Yan 			    HOTPLUGD_DOOR, strerror(errno));
102*26947304SEvan Yan 		}
103*26947304SEvan Yan 		return (B_FALSE);
104*26947304SEvan Yan 	}
105*26947304SEvan Yan 	(void) close(fd);
106*26947304SEvan Yan 
107*26947304SEvan Yan 	/* Initialize the door service */
108*26947304SEvan Yan 	if ((door_fd = door_create(door_server, NULL,
109*26947304SEvan Yan 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
110*26947304SEvan Yan 		log_err("Cannot create door service: %s\n", strerror(errno));
111*26947304SEvan Yan 		return (B_FALSE);
112*26947304SEvan Yan 	}
113*26947304SEvan Yan 
114*26947304SEvan Yan 	/* Cleanup stale door associations */
115*26947304SEvan Yan 	(void) fdetach(HOTPLUGD_DOOR);
116*26947304SEvan Yan 
117*26947304SEvan Yan 	/* Associate door service with door file */
118*26947304SEvan Yan 	if (fattach(door_fd, HOTPLUGD_DOOR) != 0) {
119*26947304SEvan Yan 		log_err("Cannot attach to door file '%s': %s\n", HOTPLUGD_DOOR,
120*26947304SEvan Yan 		    strerror(errno));
121*26947304SEvan Yan 		(void) door_revoke(door_fd);
122*26947304SEvan Yan 		(void) fdetach(HOTPLUGD_DOOR);
123*26947304SEvan Yan 		door_fd = -1;
124*26947304SEvan Yan 		return (B_FALSE);
125*26947304SEvan Yan 	}
126*26947304SEvan Yan 
127*26947304SEvan Yan 	return (B_TRUE);
128*26947304SEvan Yan }
129*26947304SEvan Yan 
130*26947304SEvan Yan /*
131*26947304SEvan Yan  * door_server_fini()
132*26947304SEvan Yan  *
133*26947304SEvan Yan  *	Terminate and cleanup the door server.
134*26947304SEvan Yan  */
135*26947304SEvan Yan void
door_server_fini(void)136*26947304SEvan Yan door_server_fini(void)
137*26947304SEvan Yan {
138*26947304SEvan Yan 	if (door_fd != -1) {
139*26947304SEvan Yan 		(void) door_revoke(door_fd);
140*26947304SEvan Yan 		(void) fdetach(HOTPLUGD_DOOR);
141*26947304SEvan Yan 	}
142*26947304SEvan Yan 
143*26947304SEvan Yan 	(void) unlink(HOTPLUGD_DOOR);
144*26947304SEvan Yan }
145*26947304SEvan Yan 
146*26947304SEvan Yan /*
147*26947304SEvan Yan  * door_server()
148*26947304SEvan Yan  *
149*26947304SEvan Yan  *	This routine is the handler which responds to each door call.
150*26947304SEvan Yan  *	Each incoming door call is expected to send a packed nvlist
151*26947304SEvan Yan  *	of arguments which describe the requested action.  And each
152*26947304SEvan Yan  *	response is sent back as a packed nvlist of results.
153*26947304SEvan Yan  *
154*26947304SEvan Yan  *	Results are always allocated on the heap.  A global list of
155*26947304SEvan Yan  *	allocated result buffers is managed, and each one is tracked
156*26947304SEvan Yan  *	by a unique sequence number.  The final step in the protocol
157*26947304SEvan Yan  *	is for the caller to send a short response using the sequence
158*26947304SEvan Yan  *	number when the buffer can be released.
159*26947304SEvan Yan  */
160*26947304SEvan Yan /*ARGSUSED*/
161*26947304SEvan Yan static void
door_server(void * cookie,char * argp,size_t sz,door_desc_t * dp,uint_t ndesc)162*26947304SEvan Yan door_server(void *cookie, char *argp, size_t sz, door_desc_t *dp, uint_t ndesc)
163*26947304SEvan Yan {
164*26947304SEvan Yan 	nvlist_t	*args = NULL;
165*26947304SEvan Yan 	nvlist_t	*results = NULL;
166*26947304SEvan Yan 	hp_cmd_t	cmd;
167*26947304SEvan Yan 	int		rv;
168*26947304SEvan Yan 
169*26947304SEvan Yan 	dprintf("Door call: cookie=%p, argp=%p, sz=%d\n", cookie, (void *)argp,
170*26947304SEvan Yan 	    sz);
171*26947304SEvan Yan 
172*26947304SEvan Yan 	/* Special case to free a results buffer */
173*26947304SEvan Yan 	if (sz == sizeof (uint64_t)) {
174*26947304SEvan Yan 		free_buffer(*(uint64_t *)(uintptr_t)argp);
175*26947304SEvan Yan 		(void) door_return(NULL, 0, NULL, 0);
176*26947304SEvan Yan 		return;
177*26947304SEvan Yan 	}
178*26947304SEvan Yan 
179*26947304SEvan Yan 	/* Unpack the arguments nvlist */
180*26947304SEvan Yan 	if (nvlist_unpack(argp, sz, &args, 0) != 0) {
181*26947304SEvan Yan 		log_err("Cannot unpack door arguments.\n");
182*26947304SEvan Yan 		rv = EINVAL;
183*26947304SEvan Yan 		goto fail;
184*26947304SEvan Yan 	}
185*26947304SEvan Yan 
186*26947304SEvan Yan 	/* Extract the requested command */
187*26947304SEvan Yan 	if (nvlist_lookup_int32(args, HPD_CMD, (int32_t *)&cmd) != 0) {
188*26947304SEvan Yan 		log_err("Cannot decode door command.\n");
189*26947304SEvan Yan 		rv = EINVAL;
190*26947304SEvan Yan 		goto fail;
191*26947304SEvan Yan 	}
192*26947304SEvan Yan 
193*26947304SEvan Yan 	/* Implement the command */
194*26947304SEvan Yan 	switch (cmd) {
195*26947304SEvan Yan 	case HP_CMD_GETINFO:
196*26947304SEvan Yan 		rv = cmd_getinfo(args, &results);
197*26947304SEvan Yan 		break;
198*26947304SEvan Yan 	case HP_CMD_CHANGESTATE:
199*26947304SEvan Yan 		rv = cmd_changestate(args, &results);
200*26947304SEvan Yan 		break;
201*26947304SEvan Yan 	case HP_CMD_SETPRIVATE:
202*26947304SEvan Yan 	case HP_CMD_GETPRIVATE:
203*26947304SEvan Yan 		rv = cmd_private(cmd, args, &results);
204*26947304SEvan Yan 		break;
205*26947304SEvan Yan 	default:
206*26947304SEvan Yan 		rv = EINVAL;
207*26947304SEvan Yan 		break;
208*26947304SEvan Yan 	}
209*26947304SEvan Yan 
210*26947304SEvan Yan 	/* The arguments nvlist is no longer needed */
211*26947304SEvan Yan 	nvlist_free(args);
212*26947304SEvan Yan 	args = NULL;
213*26947304SEvan Yan 
214*26947304SEvan Yan 	/*
215*26947304SEvan Yan 	 * If an nvlist was constructed for the results,
216*26947304SEvan Yan 	 * then pack the results nvlist and return it.
217*26947304SEvan Yan 	 */
218*26947304SEvan Yan 	if (results != NULL) {
219*26947304SEvan Yan 		uint64_t	seqnum;
220*26947304SEvan Yan 		char		*buf = NULL;
221*26947304SEvan Yan 		size_t		len = 0;
222*26947304SEvan Yan 
223*26947304SEvan Yan 		/* Add a sequence number to the results */
224*26947304SEvan Yan 		seqnum = get_seqnum();
225*26947304SEvan Yan 		if (nvlist_add_uint64(results, HPD_SEQNUM, seqnum) != 0) {
226*26947304SEvan Yan 			log_err("Cannot add sequence number.\n");
227*26947304SEvan Yan 			rv = EFAULT;
228*26947304SEvan Yan 			goto fail;
229*26947304SEvan Yan 		}
230*26947304SEvan Yan 
231*26947304SEvan Yan 		/* Pack the results nvlist */
232*26947304SEvan Yan 		if (nvlist_pack(results, &buf, &len,
233*26947304SEvan Yan 		    NV_ENCODE_NATIVE, 0) != 0) {
234*26947304SEvan Yan 			log_err("Cannot pack door results.\n");
235*26947304SEvan Yan 			rv = EFAULT;
236*26947304SEvan Yan 			goto fail;
237*26947304SEvan Yan 		}
238*26947304SEvan Yan 
239*26947304SEvan Yan 		/* Link results buffer into list */
240*26947304SEvan Yan 		add_buffer(seqnum, buf);
241*26947304SEvan Yan 
242*26947304SEvan Yan 		/* The results nvlist is no longer needed */
243*26947304SEvan Yan 		nvlist_free(results);
244*26947304SEvan Yan 
245*26947304SEvan Yan 		/* Return the results */
246*26947304SEvan Yan 		(void) door_return(buf, len, NULL, 0);
247*26947304SEvan Yan 		return;
248*26947304SEvan Yan 	}
249*26947304SEvan Yan 
250*26947304SEvan Yan 	/* Return result code (when no nvlist) */
251*26947304SEvan Yan 	(void) door_return((char *)&rv, sizeof (int), NULL, 0);
252*26947304SEvan Yan 	return;
253*26947304SEvan Yan 
254*26947304SEvan Yan fail:
255*26947304SEvan Yan 	log_err("Door call failed (%s)\n", strerror(rv));
256*26947304SEvan Yan 	nvlist_free(args);
257*26947304SEvan Yan 	nvlist_free(results);
258*26947304SEvan Yan 	(void) door_return((char *)&rv, sizeof (int), NULL, 0);
259*26947304SEvan Yan }
260*26947304SEvan Yan 
261*26947304SEvan Yan /*
262*26947304SEvan Yan  * check_auth()
263*26947304SEvan Yan  *
264*26947304SEvan Yan  *	Perform an RBAC authorization check.
265*26947304SEvan Yan  */
266*26947304SEvan Yan static int
check_auth(ucred_t * ucred,const char * auth)267*26947304SEvan Yan check_auth(ucred_t *ucred, const char *auth)
268*26947304SEvan Yan {
269*26947304SEvan Yan 	struct passwd	pwd;
270*26947304SEvan Yan 	uid_t		euid;
271*26947304SEvan Yan 	char		buf[MAXPATHLEN];
272*26947304SEvan Yan 
273*26947304SEvan Yan 	euid = ucred_geteuid(ucred);
274*26947304SEvan Yan 
275*26947304SEvan Yan 	if ((getpwuid_r(euid, &pwd, buf, sizeof (buf)) == NULL) ||
276*26947304SEvan Yan 	    (chkauthattr(auth, pwd.pw_name) == 0)) {
277*26947304SEvan Yan 		log_info("Unauthorized door call.\n");
278*26947304SEvan Yan 		return (-1);
279*26947304SEvan Yan 	}
280*26947304SEvan Yan 
281*26947304SEvan Yan 	return (0);
282*26947304SEvan Yan }
283*26947304SEvan Yan 
284*26947304SEvan Yan /*
285*26947304SEvan Yan  * cmd_getinfo()
286*26947304SEvan Yan  *
287*26947304SEvan Yan  *	Implements the door command to get a hotplug information snapshot.
288*26947304SEvan Yan  */
289*26947304SEvan Yan static int
cmd_getinfo(nvlist_t * args,nvlist_t ** resultsp)290*26947304SEvan Yan cmd_getinfo(nvlist_t *args, nvlist_t **resultsp)
291*26947304SEvan Yan {
292*26947304SEvan Yan 	hp_node_t	root;
293*26947304SEvan Yan 	nvlist_t	*results;
294*26947304SEvan Yan 	char		*path;
295*26947304SEvan Yan 	char		*connection;
296*26947304SEvan Yan 	char		*buf = NULL;
297*26947304SEvan Yan 	size_t		len = 0;
298*26947304SEvan Yan 	uint_t		flags;
299*26947304SEvan Yan 	int		rv;
300*26947304SEvan Yan 
301*26947304SEvan Yan 	dprintf("cmd_getinfo:\n");
302*26947304SEvan Yan 
303*26947304SEvan Yan 	/* Get arguments */
304*26947304SEvan Yan 	if (nvlist_lookup_string(args, HPD_PATH, &path) != 0) {
305*26947304SEvan Yan 		dprintf("cmd_getinfo: invalid arguments.\n");
306*26947304SEvan Yan 		return (EINVAL);
307*26947304SEvan Yan 	}
308*26947304SEvan Yan 	if (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0)
309*26947304SEvan Yan 		connection = NULL;
310*26947304SEvan Yan 	if (nvlist_lookup_uint32(args, HPD_FLAGS, (uint32_t *)&flags) != 0)
311*26947304SEvan Yan 		flags = 0;
312*26947304SEvan Yan 
313*26947304SEvan Yan 	/* Get and pack the requested snapshot */
314*26947304SEvan Yan 	if ((rv = getinfo(path, connection, flags, &root)) == 0) {
315*26947304SEvan Yan 		rv = hp_pack(root, &buf, &len);
316*26947304SEvan Yan 		hp_fini(root);
317*26947304SEvan Yan 	}
318*26947304SEvan Yan 	dprintf("cmd_getinfo: getinfo(): rv = %d, buf = %p.\n", rv,
319*26947304SEvan Yan 	    (void *)buf);
320*26947304SEvan Yan 
321*26947304SEvan Yan 	/*
322*26947304SEvan Yan 	 * If the above failed or there is no snapshot,
323*26947304SEvan Yan 	 * then only return a status code.
324*26947304SEvan Yan 	 */
325*26947304SEvan Yan 	if (rv != 0)
326*26947304SEvan Yan 		return (rv);
327*26947304SEvan Yan 	if (buf == NULL)
328*26947304SEvan Yan 		return (EFAULT);
329*26947304SEvan Yan 
330*26947304SEvan Yan 	/* Allocate nvlist for results */
331*26947304SEvan Yan 	if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) {
332*26947304SEvan Yan 		dprintf("cmd_getinfo: nvlist_alloc() failed.\n");
333*26947304SEvan Yan 		free(buf);
334*26947304SEvan Yan 		return (ENOMEM);
335*26947304SEvan Yan 	}
336*26947304SEvan Yan 
337*26947304SEvan Yan 	/* Add snapshot and successful status to results */
338*26947304SEvan Yan 	if ((nvlist_add_int32(results, HPD_STATUS, 0) != 0) ||
339*26947304SEvan Yan 	    (nvlist_add_byte_array(results, HPD_INFO,
340*26947304SEvan Yan 	    (uchar_t *)buf, len) != 0)) {
341*26947304SEvan Yan 		dprintf("cmd_getinfo: nvlist add failure.\n");
342*26947304SEvan Yan 		nvlist_free(results);
343*26947304SEvan Yan 		free(buf);
344*26947304SEvan Yan 		return (ENOMEM);
345*26947304SEvan Yan 	}
346*26947304SEvan Yan 
347*26947304SEvan Yan 	/* Packed snapshot no longer needed */
348*26947304SEvan Yan 	free(buf);
349*26947304SEvan Yan 
350*26947304SEvan Yan 	/* Success */
351*26947304SEvan Yan 	*resultsp = results;
352*26947304SEvan Yan 	return (0);
353*26947304SEvan Yan }
354*26947304SEvan Yan 
355*26947304SEvan Yan /*
356*26947304SEvan Yan  * cmd_changestate()
357*26947304SEvan Yan  *
358*26947304SEvan Yan  *	Implements the door command to initate a state change operation.
359*26947304SEvan Yan  *
360*26947304SEvan Yan  *	NOTE: requires 'modify' authorization.
361*26947304SEvan Yan  */
362*26947304SEvan Yan static int
cmd_changestate(nvlist_t * args,nvlist_t ** resultsp)363*26947304SEvan Yan cmd_changestate(nvlist_t *args, nvlist_t **resultsp)
364*26947304SEvan Yan {
365*26947304SEvan Yan 	hp_node_t	root = NULL;
366*26947304SEvan Yan 	nvlist_t	*results = NULL;
367*26947304SEvan Yan 	char		*path, *connection;
368*26947304SEvan Yan 	ucred_t		*uc = NULL;
369*26947304SEvan Yan 	uint_t		flags;
370*26947304SEvan Yan 	int		rv, state, old_state, status;
371*26947304SEvan Yan 
372*26947304SEvan Yan 	dprintf("cmd_changestate:\n");
373*26947304SEvan Yan 
374*26947304SEvan Yan 	/* Get arguments */
375*26947304SEvan Yan 	if ((nvlist_lookup_string(args, HPD_PATH, &path) != 0) ||
376*26947304SEvan Yan 	    (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0) ||
377*26947304SEvan Yan 	    (nvlist_lookup_int32(args, HPD_STATE, &state) != 0)) {
378*26947304SEvan Yan 		dprintf("cmd_changestate: invalid arguments.\n");
379*26947304SEvan Yan 		return (EINVAL);
380*26947304SEvan Yan 	}
381*26947304SEvan Yan 	if (nvlist_lookup_uint32(args, HPD_FLAGS, (uint32_t *)&flags) != 0)
382*26947304SEvan Yan 		flags = 0;
383*26947304SEvan Yan 
384*26947304SEvan Yan 	/* Get caller's credentials */
385*26947304SEvan Yan 	if (door_ucred(&uc) != 0) {
386*26947304SEvan Yan 		log_err("Cannot get door credentials (%s)\n", strerror(errno));
387*26947304SEvan Yan 		return (EACCES);
388*26947304SEvan Yan 	}
389*26947304SEvan Yan 
390*26947304SEvan Yan 	/* Check authorization */
391*26947304SEvan Yan 	if (check_auth(uc, HP_MODIFY_AUTH) != 0) {
392*26947304SEvan Yan 		dprintf("cmd_changestate: access denied.\n");
393*26947304SEvan Yan 		audit_changestate(uc, HP_MODIFY_AUTH, path, connection,
394*26947304SEvan Yan 		    state, -1, ADT_FAIL_VALUE_AUTH);
395*26947304SEvan Yan 		ucred_free(uc);
396*26947304SEvan Yan 		return (EACCES);
397*26947304SEvan Yan 	}
398*26947304SEvan Yan 
399*26947304SEvan Yan 	/* Perform the state change operation */
400*26947304SEvan Yan 	status = changestate(path, connection, state, flags, &old_state, &root);
401*26947304SEvan Yan 	dprintf("cmd_changestate: changestate() == %d\n", status);
402*26947304SEvan Yan 
403*26947304SEvan Yan 	/* Audit the operation */
404*26947304SEvan Yan 	audit_changestate(uc, HP_MODIFY_AUTH, path, connection, state,
405*26947304SEvan Yan 	    old_state, status);
406*26947304SEvan Yan 
407*26947304SEvan Yan 	/* Caller's credentials no longer needed */
408*26947304SEvan Yan 	ucred_free(uc);
409*26947304SEvan Yan 
410*26947304SEvan Yan 	/*
411*26947304SEvan Yan 	 * Pack the results into an nvlist if there is an error snapshot.
412*26947304SEvan Yan 	 *
413*26947304SEvan Yan 	 * If any error occurs while packing the results, the original
414*26947304SEvan Yan 	 * error code from changestate() above is still returned.
415*26947304SEvan Yan 	 */
416*26947304SEvan Yan 	if (root != NULL) {
417*26947304SEvan Yan 		char	*buf = NULL;
418*26947304SEvan Yan 		size_t	len = 0;
419*26947304SEvan Yan 
420*26947304SEvan Yan 		dprintf("cmd_changestate: results nvlist required.\n");
421*26947304SEvan Yan 
422*26947304SEvan Yan 		/* Pack and discard the error snapshot */
423*26947304SEvan Yan 		rv = hp_pack(root, &buf, &len);
424*26947304SEvan Yan 		hp_fini(root);
425*26947304SEvan Yan 		if (rv != 0) {
426*26947304SEvan Yan 			dprintf("cmd_changestate: hp_pack() failed (%s).\n",
427*26947304SEvan Yan 			    strerror(rv));
428*26947304SEvan Yan 			return (status);
429*26947304SEvan Yan 		}
430*26947304SEvan Yan 
431*26947304SEvan Yan 		/* Allocate nvlist for results */
432*26947304SEvan Yan 		if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) {
433*26947304SEvan Yan 			dprintf("cmd_changestate: nvlist_alloc() failed.\n");
434*26947304SEvan Yan 			free(buf);
435*26947304SEvan Yan 			return (status);
436*26947304SEvan Yan 		}
437*26947304SEvan Yan 
438*26947304SEvan Yan 		/* Add the results into the nvlist */
439*26947304SEvan Yan 		if ((nvlist_add_int32(results, HPD_STATUS, status) != 0) ||
440*26947304SEvan Yan 		    (nvlist_add_byte_array(results, HPD_INFO, (uchar_t *)buf,
441*26947304SEvan Yan 		    len) != 0)) {
442*26947304SEvan Yan 			dprintf("cmd_changestate: nvlist add failed.\n");
443*26947304SEvan Yan 			nvlist_free(results);
444*26947304SEvan Yan 			free(buf);
445*26947304SEvan Yan 			return (status);
446*26947304SEvan Yan 		}
447*26947304SEvan Yan 
448*26947304SEvan Yan 		*resultsp = results;
449*26947304SEvan Yan 	}
450*26947304SEvan Yan 
451*26947304SEvan Yan 	return (status);
452*26947304SEvan Yan }
453*26947304SEvan Yan 
454*26947304SEvan Yan /*
455*26947304SEvan Yan  * cmd_private()
456*26947304SEvan Yan  *
457*26947304SEvan Yan  *	Implementation of the door command to set or get bus private options.
458*26947304SEvan Yan  *
459*26947304SEvan Yan  *	NOTE: requires 'modify' authorization for the 'set' command.
460*26947304SEvan Yan  */
461*26947304SEvan Yan static int
cmd_private(hp_cmd_t cmd,nvlist_t * args,nvlist_t ** resultsp)462*26947304SEvan Yan cmd_private(hp_cmd_t cmd, nvlist_t *args, nvlist_t **resultsp)
463*26947304SEvan Yan {
464*26947304SEvan Yan 	nvlist_t	*results = NULL;
465*26947304SEvan Yan 	ucred_t		*uc = NULL;
466*26947304SEvan Yan 	char		*path, *connection, *options;
467*26947304SEvan Yan 	char		*values = NULL;
468*26947304SEvan Yan 	int		status;
469*26947304SEvan Yan 
470*26947304SEvan Yan 	dprintf("cmd_private:\n");
471*26947304SEvan Yan 
472*26947304SEvan Yan 	/* Get caller's credentials */
473*26947304SEvan Yan 	if ((cmd == HP_CMD_SETPRIVATE) && (door_ucred(&uc) != 0)) {
474*26947304SEvan Yan 		log_err("Cannot get door credentials (%s)\n", strerror(errno));
475*26947304SEvan Yan 		return (EACCES);
476*26947304SEvan Yan 	}
477*26947304SEvan Yan 
478*26947304SEvan Yan 	/* Get arguments */
479*26947304SEvan Yan 	if ((nvlist_lookup_string(args, HPD_PATH, &path) != 0) ||
480*26947304SEvan Yan 	    (nvlist_lookup_string(args, HPD_CONNECTION, &connection) != 0) ||
481*26947304SEvan Yan 	    (nvlist_lookup_string(args, HPD_OPTIONS, &options) != 0)) {
482*26947304SEvan Yan 		dprintf("cmd_private: invalid arguments.\n");
483*26947304SEvan Yan 		return (EINVAL);
484*26947304SEvan Yan 	}
485*26947304SEvan Yan 
486*26947304SEvan Yan 	/* Check authorization */
487*26947304SEvan Yan 	if ((cmd == HP_CMD_SETPRIVATE) &&
488*26947304SEvan Yan 	    (check_auth(uc, HP_MODIFY_AUTH) != 0)) {
489*26947304SEvan Yan 		dprintf("cmd_private: access denied.\n");
490*26947304SEvan Yan 		audit_setprivate(uc, HP_MODIFY_AUTH, path, connection, options,
491*26947304SEvan Yan 		    ADT_FAIL_VALUE_AUTH);
492*26947304SEvan Yan 		ucred_free(uc);
493*26947304SEvan Yan 		return (EACCES);
494*26947304SEvan Yan 	}
495*26947304SEvan Yan 
496*26947304SEvan Yan 	/* Perform the operation */
497*26947304SEvan Yan 	status = private_options(path, connection, cmd, options, &values);
498*26947304SEvan Yan 	dprintf("cmd_private: private_options() == %d\n", status);
499*26947304SEvan Yan 
500*26947304SEvan Yan 	/* Audit the operation */
501*26947304SEvan Yan 	if (cmd == HP_CMD_SETPRIVATE) {
502*26947304SEvan Yan 		audit_setprivate(uc, HP_MODIFY_AUTH, path, connection, options,
503*26947304SEvan Yan 		    status);
504*26947304SEvan Yan 		ucred_free(uc);
505*26947304SEvan Yan 	}
506*26947304SEvan Yan 
507*26947304SEvan Yan 	/* Construct an nvlist if values were returned */
508*26947304SEvan Yan 	if (values != NULL) {
509*26947304SEvan Yan 
510*26947304SEvan Yan 		/* Allocate nvlist for results */
511*26947304SEvan Yan 		if (nvlist_alloc(&results, NV_UNIQUE_NAME_TYPE, 0) != 0) {
512*26947304SEvan Yan 			dprintf("cmd_private: nvlist_alloc() failed.\n");
513*26947304SEvan Yan 			free(values);
514*26947304SEvan Yan 			return (ENOMEM);
515*26947304SEvan Yan 		}
516*26947304SEvan Yan 
517*26947304SEvan Yan 		/* Add values and status to the results */
518*26947304SEvan Yan 		if ((nvlist_add_int32(results, HPD_STATUS, status) != 0) ||
519*26947304SEvan Yan 		    (nvlist_add_string(results, HPD_OPTIONS, values) != 0)) {
520*26947304SEvan Yan 			dprintf("cmd_private: nvlist add failed.\n");
521*26947304SEvan Yan 			nvlist_free(results);
522*26947304SEvan Yan 			free(values);
523*26947304SEvan Yan 			return (ENOMEM);
524*26947304SEvan Yan 		}
525*26947304SEvan Yan 
526*26947304SEvan Yan 		/* The values string is no longer needed */
527*26947304SEvan Yan 		free(values);
528*26947304SEvan Yan 
529*26947304SEvan Yan 		*resultsp = results;
530*26947304SEvan Yan 	}
531*26947304SEvan Yan 
532*26947304SEvan Yan 	return (status);
533*26947304SEvan Yan }
534*26947304SEvan Yan 
535*26947304SEvan Yan /*
536*26947304SEvan Yan  * get_seqnum()
537*26947304SEvan Yan  *
538*26947304SEvan Yan  *	Allocate the next unique sequence number for a results buffer.
539*26947304SEvan Yan  */
540*26947304SEvan Yan static uint64_t
get_seqnum(void)541*26947304SEvan Yan get_seqnum(void)
542*26947304SEvan Yan {
543*26947304SEvan Yan 	uint64_t seqnum;
544*26947304SEvan Yan 
545*26947304SEvan Yan 	(void) pthread_mutex_lock(&buffer_lock);
546*26947304SEvan Yan 
547*26947304SEvan Yan 	seqnum = buffer_seqnum++;
548*26947304SEvan Yan 
549*26947304SEvan Yan 	(void) pthread_mutex_unlock(&buffer_lock);
550*26947304SEvan Yan 
551*26947304SEvan Yan 	return (seqnum);
552*26947304SEvan Yan }
553*26947304SEvan Yan 
554*26947304SEvan Yan /*
555*26947304SEvan Yan  * add_buffer()
556*26947304SEvan Yan  *
557*26947304SEvan Yan  *	Link a results buffer into the list containing all buffers.
558*26947304SEvan Yan  */
559*26947304SEvan Yan static void
add_buffer(uint64_t seqnum,char * buf)560*26947304SEvan Yan add_buffer(uint64_t seqnum, char *buf)
561*26947304SEvan Yan {
562*26947304SEvan Yan 	i_buffer_t	*node;
563*26947304SEvan Yan 
564*26947304SEvan Yan 	if ((node = (i_buffer_t *)malloc(sizeof (i_buffer_t))) == NULL) {
565*26947304SEvan Yan 		/* The consequence is a memory leak. */
566*26947304SEvan Yan 		log_err("Cannot allocate results buffer: %s\n",
567*26947304SEvan Yan 		    strerror(errno));
568*26947304SEvan Yan 		return;
569*26947304SEvan Yan 	}
570*26947304SEvan Yan 
571*26947304SEvan Yan 	node->seqnum = seqnum;
572*26947304SEvan Yan 	node->buffer = buf;
573*26947304SEvan Yan 
574*26947304SEvan Yan 	(void) pthread_mutex_lock(&buffer_lock);
575*26947304SEvan Yan 
576*26947304SEvan Yan 	node->next = buffer_list;
577*26947304SEvan Yan 	buffer_list = node;
578*26947304SEvan Yan 
579*26947304SEvan Yan 	(void) pthread_mutex_unlock(&buffer_lock);
580*26947304SEvan Yan }
581*26947304SEvan Yan 
582*26947304SEvan Yan /*
583*26947304SEvan Yan  * free_buffer()
584*26947304SEvan Yan  *
585*26947304SEvan Yan  *	Remove a results buffer from the list containing all buffers.
586*26947304SEvan Yan  */
587*26947304SEvan Yan static void
free_buffer(uint64_t seqnum)588*26947304SEvan Yan free_buffer(uint64_t seqnum)
589*26947304SEvan Yan {
590*26947304SEvan Yan 	i_buffer_t	*node, *prev;
591*26947304SEvan Yan 
592*26947304SEvan Yan 	(void) pthread_mutex_lock(&buffer_lock);
593*26947304SEvan Yan 
594*26947304SEvan Yan 	prev = NULL;
595*26947304SEvan Yan 	node = buffer_list;
596*26947304SEvan Yan 
597*26947304SEvan Yan 	while (node) {
598*26947304SEvan Yan 		if (node->seqnum == seqnum) {
599*26947304SEvan Yan 			dprintf("Free buffer %lld\n", seqnum);
600*26947304SEvan Yan 			if (prev) {
601*26947304SEvan Yan 				prev->next = node->next;
602*26947304SEvan Yan 			} else {
603*26947304SEvan Yan 				buffer_list = node->next;
604*26947304SEvan Yan 			}
605*26947304SEvan Yan 			free(node->buffer);
606*26947304SEvan Yan 			free(node);
607*26947304SEvan Yan 			break;
608*26947304SEvan Yan 		}
609*26947304SEvan Yan 		prev = node;
610*26947304SEvan Yan 		node = node->next;
611*26947304SEvan Yan 	}
612*26947304SEvan Yan 
613*26947304SEvan Yan 	(void) pthread_mutex_unlock(&buffer_lock);
614*26947304SEvan Yan }
615*26947304SEvan Yan 
616*26947304SEvan Yan /*
617*26947304SEvan Yan  * audit_session()
618*26947304SEvan Yan  *
619*26947304SEvan Yan  *	Initialize an audit session.
620*26947304SEvan Yan  */
621*26947304SEvan Yan static int
audit_session(ucred_t * ucred,adt_session_data_t ** sessionp)622*26947304SEvan Yan audit_session(ucred_t *ucred, adt_session_data_t **sessionp)
623*26947304SEvan Yan {
624*26947304SEvan Yan 	adt_session_data_t	*session;
625*26947304SEvan Yan 
626*26947304SEvan Yan 	if (adt_start_session(&session, NULL, 0) != 0) {
627*26947304SEvan Yan 		log_err("Cannot start audit session.\n");
628*26947304SEvan Yan 		return (-1);
629*26947304SEvan Yan 	}
630*26947304SEvan Yan 
631*26947304SEvan Yan 	if (adt_set_from_ucred(session, ucred, ADT_NEW) != 0) {
632*26947304SEvan Yan 		log_err("Cannot set audit session from ucred.\n");
633*26947304SEvan Yan 		(void) adt_end_session(session);
634*26947304SEvan Yan 		return (-1);
635*26947304SEvan Yan 	}
636*26947304SEvan Yan 
637*26947304SEvan Yan 	*sessionp = session;
638*26947304SEvan Yan 	return (0);
639*26947304SEvan Yan }
640*26947304SEvan Yan 
641*26947304SEvan Yan /*
642*26947304SEvan Yan  * audit_changestate()
643*26947304SEvan Yan  *
644*26947304SEvan Yan  *	Audit a 'changestate' door command.
645*26947304SEvan Yan  */
646*26947304SEvan Yan static void
audit_changestate(ucred_t * ucred,char * auth,char * path,char * connection,int new_state,int old_state,int result)647*26947304SEvan Yan audit_changestate(ucred_t *ucred, char *auth, char *path, char *connection,
648*26947304SEvan Yan     int new_state, int old_state, int result)
649*26947304SEvan Yan {
650*26947304SEvan Yan 	adt_session_data_t	*session;
651*26947304SEvan Yan 	adt_event_data_t	*event;
652*26947304SEvan Yan 	int			pass_fail, fail_reason;
653*26947304SEvan Yan 
654*26947304SEvan Yan 	if (audit_session(ucred, &session) != 0)
655*26947304SEvan Yan 		return;
656*26947304SEvan Yan 
657*26947304SEvan Yan 	if ((event = adt_alloc_event(session, ADT_hotplug_state)) == NULL) {
658*26947304SEvan Yan 		(void) adt_end_session(session);
659*26947304SEvan Yan 		return;
660*26947304SEvan Yan 	}
661*26947304SEvan Yan 
662*26947304SEvan Yan 	if (result == 0) {
663*26947304SEvan Yan 		pass_fail = ADT_SUCCESS;
664*26947304SEvan Yan 		fail_reason = ADT_SUCCESS;
665*26947304SEvan Yan 	} else {
666*26947304SEvan Yan 		pass_fail = ADT_FAILURE;
667*26947304SEvan Yan 		fail_reason = result;
668*26947304SEvan Yan 	}
669*26947304SEvan Yan 
670*26947304SEvan Yan 	event->adt_hotplug_state.auth_used = auth;
671*26947304SEvan Yan 	event->adt_hotplug_state.device_path = path;
672*26947304SEvan Yan 	event->adt_hotplug_state.connection = connection;
673*26947304SEvan Yan 	event->adt_hotplug_state.new_state = state_str(new_state);
674*26947304SEvan Yan 	event->adt_hotplug_state.old_state = state_str(old_state);
675*26947304SEvan Yan 
676*26947304SEvan Yan 	/* Put the event */
677*26947304SEvan Yan 	if (adt_put_event(event, pass_fail, fail_reason) != 0)
678*26947304SEvan Yan 		log_err("Cannot put audit event.\n");
679*26947304SEvan Yan 
680*26947304SEvan Yan 	adt_free_event(event);
681*26947304SEvan Yan 	(void) adt_end_session(session);
682*26947304SEvan Yan }
683*26947304SEvan Yan 
684*26947304SEvan Yan /*
685*26947304SEvan Yan  * audit_setprivate()
686*26947304SEvan Yan  *
687*26947304SEvan Yan  *	Audit a 'set private' door command.
688*26947304SEvan Yan  */
689*26947304SEvan Yan static void
audit_setprivate(ucred_t * ucred,char * auth,char * path,char * connection,char * options,int result)690*26947304SEvan Yan audit_setprivate(ucred_t *ucred, char *auth, char *path, char *connection,
691*26947304SEvan Yan     char *options, int result)
692*26947304SEvan Yan {
693*26947304SEvan Yan 	adt_session_data_t	*session;
694*26947304SEvan Yan 	adt_event_data_t	*event;
695*26947304SEvan Yan 	int			pass_fail, fail_reason;
696*26947304SEvan Yan 
697*26947304SEvan Yan 	if (audit_session(ucred, &session) != 0)
698*26947304SEvan Yan 		return;
699*26947304SEvan Yan 
700*26947304SEvan Yan 	if ((event = adt_alloc_event(session, ADT_hotplug_set)) == NULL) {
701*26947304SEvan Yan 		(void) adt_end_session(session);
702*26947304SEvan Yan 		return;
703*26947304SEvan Yan 	}
704*26947304SEvan Yan 
705*26947304SEvan Yan 	if (result == 0) {
706*26947304SEvan Yan 		pass_fail = ADT_SUCCESS;
707*26947304SEvan Yan 		fail_reason = ADT_SUCCESS;
708*26947304SEvan Yan 	} else {
709*26947304SEvan Yan 		pass_fail = ADT_FAILURE;
710*26947304SEvan Yan 		fail_reason = result;
711*26947304SEvan Yan 	}
712*26947304SEvan Yan 
713*26947304SEvan Yan 	event->adt_hotplug_set.auth_used = auth;
714*26947304SEvan Yan 	event->adt_hotplug_set.device_path = path;
715*26947304SEvan Yan 	event->adt_hotplug_set.connection = connection;
716*26947304SEvan Yan 	event->adt_hotplug_set.options = options;
717*26947304SEvan Yan 
718*26947304SEvan Yan 	/* Put the event */
719*26947304SEvan Yan 	if (adt_put_event(event, pass_fail, fail_reason) != 0)
720*26947304SEvan Yan 		log_err("Cannot put audit event.\n");
721*26947304SEvan Yan 
722*26947304SEvan Yan 	adt_free_event(event);
723*26947304SEvan Yan 	(void) adt_end_session(session);
724*26947304SEvan Yan }
725*26947304SEvan Yan 
726*26947304SEvan Yan /*
727*26947304SEvan Yan  * state_str()
728*26947304SEvan Yan  *
729*26947304SEvan Yan  *	Convert a state from integer to string.
730*26947304SEvan Yan  */
731*26947304SEvan Yan static char *
state_str(int state)732*26947304SEvan Yan state_str(int state)
733*26947304SEvan Yan {
734*26947304SEvan Yan 	switch (state) {
735*26947304SEvan Yan 	case DDI_HP_CN_STATE_EMPTY:
736*26947304SEvan Yan 		return ("EMPTY");
737*26947304SEvan Yan 	case DDI_HP_CN_STATE_PRESENT:
738*26947304SEvan Yan 		return ("PRESENT");
739*26947304SEvan Yan 	case DDI_HP_CN_STATE_POWERED:
740*26947304SEvan Yan 		return ("POWERED");
741*26947304SEvan Yan 	case DDI_HP_CN_STATE_ENABLED:
742*26947304SEvan Yan 		return ("ENABLED");
743*26947304SEvan Yan 	case DDI_HP_CN_STATE_PORT_EMPTY:
744*26947304SEvan Yan 		return ("PORT-EMPTY");
745*26947304SEvan Yan 	case DDI_HP_CN_STATE_PORT_PRESENT:
746*26947304SEvan Yan 		return ("PORT-PRESENT");
747*26947304SEvan Yan 	case DDI_HP_CN_STATE_OFFLINE:
748*26947304SEvan Yan 		return ("OFFLINE");
749*26947304SEvan Yan 	case DDI_HP_CN_STATE_ATTACHED:
750*26947304SEvan Yan 		return ("ATTACHED");
751*26947304SEvan Yan 	case DDI_HP_CN_STATE_MAINTENANCE:
752*26947304SEvan Yan 		return ("MAINTENANCE");
753*26947304SEvan Yan 	case DDI_HP_CN_STATE_ONLINE:
754*26947304SEvan Yan 		return ("ONLINE");
755*26947304SEvan Yan 	default:
756*26947304SEvan Yan 		return ("UNKNOWN");
757*26947304SEvan Yan 	}
758*26947304SEvan Yan }
759