xref: /illumos-gate/usr/src/lib/libipmi/common/ipmi_user.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
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 
31 typedef 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 
42 typedef 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 
53 typedef 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 
89 typedef 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 
105 static ipmi_get_user_access_t *
106 ipmi_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 
141 static const char *
142 ipmi_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 
163 void
164 ipmi_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  */
178 int
179 ipmi_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 
264 typedef 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 
270 static int
271 ipmi_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 
288 ipmi_user_t *
289 ipmi_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 
306 ipmi_user_t *
307 ipmi_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 
324 int
325 ipmi_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