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 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <libipmi.h>
27#include <string.h>
28
29#include "ipmi_impl.h"
30
31typedef struct ipmi_user_impl {
32	ipmi_list_t	iu_list;
33	ipmi_user_t	iu_user;
34} ipmi_user_impl_t;
35
36/*
37 * Get User Access.  See section 22.27.
38 *
39 * See libipmi.h for a complete description of IPMI reference material.
40 */
41
42typedef struct ipmi_get_user_access_req {
43	DECL_BITFIELD2(
44	    igua_channel		:4,
45	    __reserved1			:4);
46	DECL_BITFIELD2(
47	    igua_uid			:2,
48	    __reserved2			:6);
49} ipmi_get_user_access_req_t;
50
51#define	IPMI_CMD_GET_USER_ACCESS	0x44
52
53typedef struct ipmi_get_user_access {
54	DECL_BITFIELD2(
55	    igua_max_uid		:4,
56	    __reserved1			:4);
57	DECL_BITFIELD2(
58	    igua_enable_status		:4,
59	    igua_enabled_uid		:4);
60	DECL_BITFIELD2(
61	    __reserved2			:4,
62	    igua_fixed_uid		:4);
63	DECL_BITFIELD5(
64	    __reserved3			:1,
65	    igua_only_callback		:1,
66	    igua_link_auth_enable	:1,
67	    igua_ipmi_msg_enable	:1,
68	    igua_privilege_level	:4);
69} ipmi_get_user_access_t;
70
71#define	IPMI_USER_ENABLE_UNSPECIFIED	0x00
72#define	IPMI_USER_ENABLE_SETPASSWD	0x01
73#define	IPMI_USER_DISABLE_SETPASSWD	0x02
74
75#define	IPMI_USER_CHANNEL_CURRENT	0xe
76
77/*
78 * Get User Name.  See section 22.29
79 */
80
81#define	IPMI_CMD_GET_USER_NAME		0x46
82
83/*
84 * Set User Password.  See section 22.30
85 */
86
87#define	IPMI_CMD_SET_USER_PASSWORD	0x47
88
89typedef struct ipmi_set_user_password {
90	DECL_BITFIELD3(
91	    isup_uid		:6,
92	    __reserved1		:1,
93	    isup_len20		:1);
94	DECL_BITFIELD2(
95	    isup_op		:2,
96	    __reserved2		:6);
97	char		isup_passwd[20];
98} ipmi_set_user_password_t;
99
100#define	IPMI_PASSWORD_OP_DISABLE	0x0
101#define	IPMI_PASSWORD_OP_ENABLE		0x1
102#define	IPMI_PASSWORD_OP_SET		0x2
103#define	IPMI_PASSWORD_OP_TEST		0x3
104
105static ipmi_get_user_access_t *
106ipmi_get_user_access(ipmi_handle_t *ihp, uint8_t channel, uint8_t uid)
107{
108	ipmi_cmd_t cmd, *resp;
109	ipmi_get_user_access_req_t req = { 0 };
110
111	req.igua_channel = channel;
112	req.igua_uid = uid;
113
114	cmd.ic_netfn = IPMI_NETFN_APP;
115	cmd.ic_cmd = IPMI_CMD_GET_USER_ACCESS;
116	cmd.ic_lun = 0;
117	cmd.ic_data = &req;
118	cmd.ic_dlen = sizeof (req);
119
120	if ((resp = ipmi_send(ihp, &cmd)) == NULL) {
121		/*
122		 * If sessions aren't supported on the current channel, some
123		 * service processors (notably Sun's ILOM) will return an
124		 * invalid request completion code (0xCC).  For these SPs, we
125		 * translate this to the more appropriate EIPMI_INVALID_COMMAND.
126		 */
127		if (ipmi_errno(ihp) == EIPMI_INVALID_REQUEST)
128			(void) ipmi_set_error(ihp, EIPMI_INVALID_COMMAND,
129			    NULL);
130		return (NULL);
131	}
132
133	if (resp->ic_dlen < sizeof (ipmi_get_user_access_t)) {
134		(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
135		return (NULL);
136	}
137
138	return (resp->ic_data);
139}
140
141static const char *
142ipmi_get_user_name(ipmi_handle_t *ihp, uint8_t uid)
143{
144	ipmi_cmd_t cmd, *resp;
145
146	cmd.ic_netfn = IPMI_NETFN_APP;
147	cmd.ic_cmd = IPMI_CMD_GET_USER_NAME;
148	cmd.ic_lun = 0;
149	cmd.ic_data = &uid;
150	cmd.ic_dlen = sizeof (uid);
151
152	if ((resp = ipmi_send(ihp, &cmd)) == NULL)
153		return (NULL);
154
155	if (resp->ic_dlen < 16) {
156		(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
157		return (NULL);
158	}
159
160	return (resp->ic_data);
161}
162
163void
164ipmi_user_clear(ipmi_handle_t *ihp)
165{
166	ipmi_user_impl_t *uip;
167
168	while ((uip = ipmi_list_next(&ihp->ih_users)) != NULL) {
169		ipmi_list_delete(&ihp->ih_users, uip);
170		ipmi_free(ihp, uip->iu_user.iu_name);
171		ipmi_free(ihp, uip);
172	}
173}
174
175/*
176 * Returns user information in a well-defined structure.
177 */
178int
179ipmi_user_iter(ipmi_handle_t *ihp, int (*func)(ipmi_user_t *, void *),
180    void *data)
181{
182	ipmi_get_user_access_t *resp;
183	uint8_t i, uid_max;
184	ipmi_user_impl_t *uip;
185	ipmi_user_t *up;
186	const char *name;
187	uint8_t channel;
188	ipmi_deviceid_t *devid;
189
190	ipmi_user_clear(ihp);
191
192	channel = IPMI_USER_CHANNEL_CURRENT;
193
194	/*
195	 * Get the number of active users on the system by requesting the first
196	 * user ID (1).
197	 */
198	if ((resp = ipmi_get_user_access(ihp, channel, 1)) == NULL) {
199		/*
200		 * Some versions of the Sun ILOM have a bug which prevent the
201		 * GET USER ACCESS command from succeeding over the default
202		 * channel.  If this fails and we are on ILOM, then attempt to
203		 * use the standard channel (1) instead.
204		 */
205		if ((devid = ipmi_get_deviceid(ihp)) == NULL)
206			return (-1);
207
208		if (!ipmi_is_sun_ilom(devid))
209			return (-1);
210
211		channel = 1;
212		if ((resp = ipmi_get_user_access(ihp, channel, 1)) == NULL)
213			return (-1);
214	}
215
216	uid_max = resp->igua_max_uid;
217	for (i = 1; i <= uid_max; i++) {
218		if (i != 1 && (resp = ipmi_get_user_access(ihp,
219		    channel, i)) == NULL)
220			return (-1);
221
222		if ((uip = ipmi_zalloc(ihp, sizeof (ipmi_user_impl_t))) == NULL)
223			return (-1);
224
225		up = &uip->iu_user;
226
227		up->iu_enabled = resp->igua_enabled_uid;
228		up->iu_uid = i;
229		up->iu_ipmi_msg_enable = resp->igua_ipmi_msg_enable;
230		up->iu_link_auth_enable = resp->igua_link_auth_enable;
231		up->iu_priv = resp->igua_privilege_level;
232
233		ipmi_list_append(&ihp->ih_users, uip);
234
235		/*
236		 * If we are requesting a username that doesn't have a
237		 * supported username, we may get an INVALID REQUEST response.
238		 * If this is the case, then continue as if there is no known
239		 * username.
240		 */
241		if ((name = ipmi_get_user_name(ihp, i)) == NULL) {
242			if (ipmi_errno(ihp) == EIPMI_INVALID_REQUEST)
243				continue;
244			else
245				return (-1);
246		}
247
248		if (*name == '\0')
249			continue;
250
251		if ((up->iu_name = ipmi_strdup(ihp, name)) == NULL)
252			return (-1);
253	}
254
255	for (uip = ipmi_list_next(&ihp->ih_users); uip != NULL;
256	    uip = ipmi_list_next(uip)) {
257		if (func(&uip->iu_user, data) != 0)
258			return (-1);
259	}
260
261	return (0);
262}
263
264typedef struct ipmi_user_cb {
265	const char	*uic_name;
266	uint8_t		uic_uid;
267	ipmi_user_t	*uic_result;
268} ipmi_user_cb_t;
269
270static int
271ipmi_user_callback(ipmi_user_t *up, void *data)
272{
273	ipmi_user_cb_t *cbp = data;
274
275	if (cbp->uic_result != NULL)
276		return (0);
277
278	if (up->iu_name) {
279		if (strcmp(up->iu_name, cbp->uic_name) == 0)
280			cbp->uic_result = up;
281	} else if (up->iu_uid == cbp->uic_uid) {
282		cbp->uic_result = up;
283	}
284
285	return (0);
286}
287
288ipmi_user_t *
289ipmi_user_lookup_name(ipmi_handle_t *ihp, const char *name)
290{
291	ipmi_user_cb_t cb = { 0 };
292
293	cb.uic_name = name;
294	cb.uic_result = NULL;
295
296	if (ipmi_user_iter(ihp, ipmi_user_callback, &cb) != 0)
297		return (NULL);
298
299	if (cb.uic_result == NULL)
300		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT,
301		    "no such user");
302
303	return (cb.uic_result);
304}
305
306ipmi_user_t *
307ipmi_user_lookup_id(ipmi_handle_t *ihp, uint8_t uid)
308{
309	ipmi_user_cb_t cb = { 0 };
310
311	cb.uic_uid = uid;
312	cb.uic_result = NULL;
313
314	if (ipmi_user_iter(ihp, ipmi_user_callback, &cb) != 0)
315		return (NULL);
316
317	if (cb.uic_result == NULL)
318		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT,
319		    "no such user");
320
321	return (cb.uic_result);
322}
323
324int
325ipmi_user_set_password(ipmi_handle_t *ihp, uint8_t uid, const char *passwd)
326{
327	ipmi_set_user_password_t req = { 0 };
328	ipmi_cmd_t cmd;
329
330	req.isup_uid = uid;
331	req.isup_op = IPMI_PASSWORD_OP_SET;
332
333	if (strlen(passwd) > 19)
334		return (ipmi_set_error(ihp, EIPMI_INVALID_REQUEST,
335		    "password length must be less than 20 characters"));
336
337	if (strlen(passwd) > 15)
338		req.isup_len20 = 1;
339
340	(void) strcpy(req.isup_passwd, passwd);
341
342	cmd.ic_netfn = IPMI_NETFN_APP;
343	cmd.ic_cmd = IPMI_CMD_SET_USER_PASSWORD;
344	cmd.ic_lun = 0;
345	cmd.ic_data = &req;
346	if (req.isup_len20)
347		cmd.ic_dlen = sizeof (req);
348	else
349		cmd.ic_dlen = sizeof (req) - 4;
350
351	if (ipmi_send(ihp, &cmd) == NULL)
352		return (-1);
353
354	return (0);
355}
356