xref: /illumos-gate/usr/src/lib/libipmi/common/ipmi_user.c (revision a1ec709f09b030b52805b585412bd7c449af04b7)
11af98250Seschrock /*
21af98250Seschrock  * CDDL HEADER START
31af98250Seschrock  *
41af98250Seschrock  * The contents of this file are subject to the terms of the
51af98250Seschrock  * Common Development and Distribution License (the "License").
61af98250Seschrock  * You may not use this file except in compliance with the License.
71af98250Seschrock  *
81af98250Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91af98250Seschrock  * or http://www.opensolaris.org/os/licensing.
101af98250Seschrock  * See the License for the specific language governing permissions
111af98250Seschrock  * and limitations under the License.
121af98250Seschrock  *
131af98250Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
141af98250Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151af98250Seschrock  * If applicable, add the following below this CDDL HEADER, with the
161af98250Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
171af98250Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
181af98250Seschrock  *
191af98250Seschrock  * CDDL HEADER END
201af98250Seschrock  */
211af98250Seschrock /*
22*a1ec709fSEric Schrock  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
231af98250Seschrock  * Use is subject to license terms.
241af98250Seschrock  */
251af98250Seschrock 
261af98250Seschrock #include <libipmi.h>
271af98250Seschrock #include <string.h>
281af98250Seschrock 
291af98250Seschrock #include "ipmi_impl.h"
301af98250Seschrock 
312eeaed14Srobj typedef struct ipmi_user_impl {
322eeaed14Srobj 	ipmi_list_t	iu_list;
332eeaed14Srobj 	ipmi_user_t	iu_user;
342eeaed14Srobj } ipmi_user_impl_t;
352eeaed14Srobj 
361af98250Seschrock /*
371af98250Seschrock  * Get User Access.  See section 22.27.
381af98250Seschrock  *
391af98250Seschrock  * See libipmi.h for a complete description of IPMI reference material.
401af98250Seschrock  */
411af98250Seschrock 
421af98250Seschrock typedef struct ipmi_get_user_access_req {
432c32020fSeschrock 	DECL_BITFIELD2(
442c32020fSeschrock 	    igua_channel		:4,
452c32020fSeschrock 	    __reserved1			:4);
462c32020fSeschrock 	DECL_BITFIELD2(
472c32020fSeschrock 	    igua_uid			:2,
482c32020fSeschrock 	    __reserved2			:6);
491af98250Seschrock } ipmi_get_user_access_req_t;
501af98250Seschrock 
511af98250Seschrock #define	IPMI_CMD_GET_USER_ACCESS	0x44
521af98250Seschrock 
531af98250Seschrock typedef struct ipmi_get_user_access {
542c32020fSeschrock 	DECL_BITFIELD2(
552c32020fSeschrock 	    igua_max_uid		:4,
562c32020fSeschrock 	    __reserved1			:4);
572c32020fSeschrock 	DECL_BITFIELD2(
582c32020fSeschrock 	    igua_enable_status		:4,
592c32020fSeschrock 	    igua_enabled_uid		:4);
602c32020fSeschrock 	DECL_BITFIELD2(
612c32020fSeschrock 	    __reserved2			:4,
622c32020fSeschrock 	    igua_fixed_uid		:4);
632c32020fSeschrock 	DECL_BITFIELD5(
642c32020fSeschrock 	    __reserved3			:1,
652c32020fSeschrock 	    igua_only_callback		:1,
662c32020fSeschrock 	    igua_link_auth_enable	:1,
672c32020fSeschrock 	    igua_ipmi_msg_enable	:1,
682c32020fSeschrock 	    igua_privilege_level	:4);
691af98250Seschrock } ipmi_get_user_access_t;
701af98250Seschrock 
711af98250Seschrock #define	IPMI_USER_ENABLE_UNSPECIFIED	0x00
721af98250Seschrock #define	IPMI_USER_ENABLE_SETPASSWD	0x01
731af98250Seschrock #define	IPMI_USER_DISABLE_SETPASSWD	0x02
741af98250Seschrock 
751af98250Seschrock #define	IPMI_USER_CHANNEL_CURRENT	0xe
761af98250Seschrock 
771af98250Seschrock /*
781af98250Seschrock  * Get User Name.  See section 22.29
791af98250Seschrock  */
801af98250Seschrock 
811af98250Seschrock #define	IPMI_CMD_GET_USER_NAME		0x46
821af98250Seschrock 
831af98250Seschrock /*
841af98250Seschrock  * Set User Password.  See section 22.30
851af98250Seschrock  */
861af98250Seschrock 
871af98250Seschrock #define	IPMI_CMD_SET_USER_PASSWORD	0x47
881af98250Seschrock 
891af98250Seschrock typedef struct ipmi_set_user_password {
902c32020fSeschrock 	DECL_BITFIELD3(
912c32020fSeschrock 	    isup_uid		:6,
922c32020fSeschrock 	    __reserved1		:1,
932c32020fSeschrock 	    isup_len20		:1);
942c32020fSeschrock 	DECL_BITFIELD2(
952c32020fSeschrock 	    isup_op		:2,
962c32020fSeschrock 	    __reserved2		:6);
971af98250Seschrock 	char		isup_passwd[20];
981af98250Seschrock } ipmi_set_user_password_t;
991af98250Seschrock 
1001af98250Seschrock #define	IPMI_PASSWORD_OP_DISABLE	0x0
1011af98250Seschrock #define	IPMI_PASSWORD_OP_ENABLE		0x1
1021af98250Seschrock #define	IPMI_PASSWORD_OP_SET		0x2
1031af98250Seschrock #define	IPMI_PASSWORD_OP_TEST		0x3
1041af98250Seschrock 
1051af98250Seschrock static ipmi_get_user_access_t *
ipmi_get_user_access(ipmi_handle_t * ihp,uint8_t channel,uint8_t uid)1061af98250Seschrock ipmi_get_user_access(ipmi_handle_t *ihp, uint8_t channel, uint8_t uid)
1071af98250Seschrock {
1081af98250Seschrock 	ipmi_cmd_t cmd, *resp;
1091af98250Seschrock 	ipmi_get_user_access_req_t req = { 0 };
1101af98250Seschrock 
1111af98250Seschrock 	req.igua_channel = channel;
1121af98250Seschrock 	req.igua_uid = uid;
1131af98250Seschrock 
1141af98250Seschrock 	cmd.ic_netfn = IPMI_NETFN_APP;
1151af98250Seschrock 	cmd.ic_cmd = IPMI_CMD_GET_USER_ACCESS;
1161af98250Seschrock 	cmd.ic_lun = 0;
1171af98250Seschrock 	cmd.ic_data = &req;
1181af98250Seschrock 	cmd.ic_dlen = sizeof (req);
1191af98250Seschrock 
1201af98250Seschrock 	if ((resp = ipmi_send(ihp, &cmd)) == NULL) {
1211af98250Seschrock 		/*
1221af98250Seschrock 		 * If sessions aren't supported on the current channel, some
1231af98250Seschrock 		 * service processors (notably Sun's ILOM) will return an
1241af98250Seschrock 		 * invalid request completion code (0xCC).  For these SPs, we
1251af98250Seschrock 		 * translate this to the more appropriate EIPMI_INVALID_COMMAND.
1261af98250Seschrock 		 */
1271af98250Seschrock 		if (ipmi_errno(ihp) == EIPMI_INVALID_REQUEST)
1281af98250Seschrock 			(void) ipmi_set_error(ihp, EIPMI_INVALID_COMMAND,
1291af98250Seschrock 			    NULL);
1301af98250Seschrock 		return (NULL);
1311af98250Seschrock 	}
1321af98250Seschrock 
1331af98250Seschrock 	if (resp->ic_dlen < sizeof (ipmi_get_user_access_t)) {
1341af98250Seschrock 		(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
1351af98250Seschrock 		return (NULL);
1361af98250Seschrock 	}
1371af98250Seschrock 
1381af98250Seschrock 	return (resp->ic_data);
1391af98250Seschrock }
1401af98250Seschrock 
1411af98250Seschrock static const char *
ipmi_get_user_name(ipmi_handle_t * ihp,uint8_t uid)1421af98250Seschrock ipmi_get_user_name(ipmi_handle_t *ihp, uint8_t uid)
1431af98250Seschrock {
1441af98250Seschrock 	ipmi_cmd_t cmd, *resp;
1451af98250Seschrock 
1461af98250Seschrock 	cmd.ic_netfn = IPMI_NETFN_APP;
147*a1ec709fSEric Schrock 	cmd.ic_cmd = IPMI_CMD_GET_USER_NAME;
1481af98250Seschrock 	cmd.ic_lun = 0;
1491af98250Seschrock 	cmd.ic_data = &uid;
1501af98250Seschrock 	cmd.ic_dlen = sizeof (uid);
1511af98250Seschrock 
1521af98250Seschrock 	if ((resp = ipmi_send(ihp, &cmd)) == NULL)
1531af98250Seschrock 		return (NULL);
1541af98250Seschrock 
1551af98250Seschrock 	if (resp->ic_dlen < 16) {
1561af98250Seschrock 		(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
1571af98250Seschrock 		return (NULL);
1581af98250Seschrock 	}
1591af98250Seschrock 
1601af98250Seschrock 	return (resp->ic_data);
1611af98250Seschrock }
1621af98250Seschrock 
1631af98250Seschrock void
ipmi_user_clear(ipmi_handle_t * ihp)1641af98250Seschrock ipmi_user_clear(ipmi_handle_t *ihp)
1651af98250Seschrock {
1662eeaed14Srobj 	ipmi_user_impl_t *uip;
1671af98250Seschrock 
1682eeaed14Srobj 	while ((uip = ipmi_list_next(&ihp->ih_users)) != NULL) {
1692eeaed14Srobj 		ipmi_list_delete(&ihp->ih_users, uip);
1702eeaed14Srobj 		ipmi_free(ihp, uip->iu_user.iu_name);
1712eeaed14Srobj 		ipmi_free(ihp, uip);
1721af98250Seschrock 	}
1731af98250Seschrock }
1741af98250Seschrock 
1751af98250Seschrock /*
1761af98250Seschrock  * Returns user information in a well-defined structure.
1771af98250Seschrock  */
1781af98250Seschrock int
ipmi_user_iter(ipmi_handle_t * ihp,int (* func)(ipmi_user_t *,void *),void * data)1791af98250Seschrock ipmi_user_iter(ipmi_handle_t *ihp, int (*func)(ipmi_user_t *, void *),
1801af98250Seschrock     void *data)
1811af98250Seschrock {
1821af98250Seschrock 	ipmi_get_user_access_t *resp;
183*a1ec709fSEric Schrock 	uint8_t i, uid_max;
1842eeaed14Srobj 	ipmi_user_impl_t *uip;
1851af98250Seschrock 	ipmi_user_t *up;
1861af98250Seschrock 	const char *name;
187*a1ec709fSEric Schrock 	uint8_t channel;
188*a1ec709fSEric Schrock 	ipmi_deviceid_t *devid;
1891af98250Seschrock 
1901af98250Seschrock 	ipmi_user_clear(ihp);
1911af98250Seschrock 
192*a1ec709fSEric Schrock 	channel = IPMI_USER_CHANNEL_CURRENT;
193*a1ec709fSEric Schrock 
1941af98250Seschrock 	/*
195*a1ec709fSEric Schrock 	 * Get the number of active users on the system by requesting the first
196*a1ec709fSEric Schrock 	 * user ID (1).
1971af98250Seschrock 	 */
198*a1ec709fSEric Schrock 	if ((resp = ipmi_get_user_access(ihp, channel, 1)) == NULL) {
199*a1ec709fSEric Schrock 		/*
200*a1ec709fSEric Schrock 		 * Some versions of the Sun ILOM have a bug which prevent the
201*a1ec709fSEric Schrock 		 * GET USER ACCESS command from succeeding over the default
202*a1ec709fSEric Schrock 		 * channel.  If this fails and we are on ILOM, then attempt to
203*a1ec709fSEric Schrock 		 * use the standard channel (1) instead.
204*a1ec709fSEric Schrock 		 */
205*a1ec709fSEric Schrock 		if ((devid = ipmi_get_deviceid(ihp)) == NULL)
2061af98250Seschrock 			return (-1);
2071af98250Seschrock 
208*a1ec709fSEric Schrock 		if (!ipmi_is_sun_ilom(devid))
209*a1ec709fSEric Schrock 			return (-1);
210*a1ec709fSEric Schrock 
211*a1ec709fSEric Schrock 		channel = 1;
212*a1ec709fSEric Schrock 		if ((resp = ipmi_get_user_access(ihp, channel, 1)) == NULL)
213*a1ec709fSEric Schrock 			return (-1);
214*a1ec709fSEric Schrock 	}
215*a1ec709fSEric Schrock 
216*a1ec709fSEric Schrock 	uid_max = resp->igua_max_uid;
217*a1ec709fSEric Schrock 	for (i = 1; i <= uid_max; i++) {
218*a1ec709fSEric Schrock 		if (i != 1 && (resp = ipmi_get_user_access(ihp,
219*a1ec709fSEric Schrock 		    channel, i)) == NULL)
2201af98250Seschrock 			return (-1);
2211af98250Seschrock 
2222eeaed14Srobj 		if ((uip = ipmi_zalloc(ihp, sizeof (ipmi_user_impl_t))) == NULL)
2231af98250Seschrock 			return (-1);
2241af98250Seschrock 
2252eeaed14Srobj 		up = &uip->iu_user;
2262eeaed14Srobj 
2271af98250Seschrock 		up->iu_enabled = resp->igua_enabled_uid;
2281af98250Seschrock 		up->iu_uid = i;
2291af98250Seschrock 		up->iu_ipmi_msg_enable = resp->igua_ipmi_msg_enable;
2301af98250Seschrock 		up->iu_link_auth_enable = resp->igua_link_auth_enable;
2311af98250Seschrock 		up->iu_priv = resp->igua_privilege_level;
2322eeaed14Srobj 
2332eeaed14Srobj 		ipmi_list_append(&ihp->ih_users, uip);
2341af98250Seschrock 
235*a1ec709fSEric Schrock 		/*
236*a1ec709fSEric Schrock 		 * If we are requesting a username that doesn't have a
237*a1ec709fSEric Schrock 		 * supported username, we may get an INVALID REQUEST response.
238*a1ec709fSEric Schrock 		 * If this is the case, then continue as if there is no known
239*a1ec709fSEric Schrock 		 * username.
240*a1ec709fSEric Schrock 		 */
241*a1ec709fSEric Schrock 		if ((name = ipmi_get_user_name(ihp, i)) == NULL) {
242*a1ec709fSEric Schrock 			if (ipmi_errno(ihp) == EIPMI_INVALID_REQUEST)
243*a1ec709fSEric Schrock 				continue;
244*a1ec709fSEric Schrock 			else
2451af98250Seschrock 				return (-1);
246*a1ec709fSEric Schrock 		}
2471af98250Seschrock 
248*a1ec709fSEric Schrock 		if (*name == '\0')
249*a1ec709fSEric Schrock 			continue;
250*a1ec709fSEric Schrock 
251*a1ec709fSEric Schrock 		if ((up->iu_name = ipmi_strdup(ihp, name)) == NULL)
2521af98250Seschrock 			return (-1);
2531af98250Seschrock 	}
2541af98250Seschrock 
2552eeaed14Srobj 	for (uip = ipmi_list_next(&ihp->ih_users); uip != NULL;
2562eeaed14Srobj 	    uip = ipmi_list_next(uip)) {
2572eeaed14Srobj 		if (func(&uip->iu_user, data) != 0)
2581af98250Seschrock 			return (-1);
2591af98250Seschrock 	}
2601af98250Seschrock 
2611af98250Seschrock 	return (0);
2621af98250Seschrock }
2631af98250Seschrock 
2641af98250Seschrock typedef struct ipmi_user_cb {
2651af98250Seschrock 	const char	*uic_name;
2661af98250Seschrock 	uint8_t		uic_uid;
2671af98250Seschrock 	ipmi_user_t	*uic_result;
2681af98250Seschrock } ipmi_user_cb_t;
2691af98250Seschrock 
2701af98250Seschrock static int
ipmi_user_callback(ipmi_user_t * up,void * data)2711af98250Seschrock ipmi_user_callback(ipmi_user_t *up, void *data)
2721af98250Seschrock {
2731af98250Seschrock 	ipmi_user_cb_t *cbp = data;
2741af98250Seschrock 
2751af98250Seschrock 	if (cbp->uic_result != NULL)
2761af98250Seschrock 		return (0);
2771af98250Seschrock 
2781af98250Seschrock 	if (up->iu_name) {
2791af98250Seschrock 		if (strcmp(up->iu_name, cbp->uic_name) == 0)
2801af98250Seschrock 			cbp->uic_result = up;
2811af98250Seschrock 	} else if (up->iu_uid == cbp->uic_uid) {
2821af98250Seschrock 		cbp->uic_result = up;
2831af98250Seschrock 	}
2841af98250Seschrock 
2851af98250Seschrock 	return (0);
2861af98250Seschrock }
2871af98250Seschrock 
2881af98250Seschrock ipmi_user_t *
ipmi_user_lookup_name(ipmi_handle_t * ihp,const char * name)2891af98250Seschrock ipmi_user_lookup_name(ipmi_handle_t *ihp, const char *name)
2901af98250Seschrock {
2911af98250Seschrock 	ipmi_user_cb_t cb = { 0 };
2921af98250Seschrock 
2931af98250Seschrock 	cb.uic_name = name;
2941af98250Seschrock 	cb.uic_result = NULL;
2951af98250Seschrock 
2961af98250Seschrock 	if (ipmi_user_iter(ihp, ipmi_user_callback, &cb) != 0)
2971af98250Seschrock 		return (NULL);
2981af98250Seschrock 
2991af98250Seschrock 	if (cb.uic_result == NULL)
3001af98250Seschrock 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT,
3011af98250Seschrock 		    "no such user");
3021af98250Seschrock 
3031af98250Seschrock 	return (cb.uic_result);
3041af98250Seschrock }
3051af98250Seschrock 
3061af98250Seschrock ipmi_user_t *
ipmi_user_lookup_id(ipmi_handle_t * ihp,uint8_t uid)3071af98250Seschrock ipmi_user_lookup_id(ipmi_handle_t *ihp, uint8_t uid)
3081af98250Seschrock {
3091af98250Seschrock 	ipmi_user_cb_t cb = { 0 };
3101af98250Seschrock 
3111af98250Seschrock 	cb.uic_uid = uid;
3121af98250Seschrock 	cb.uic_result = NULL;
3131af98250Seschrock 
3141af98250Seschrock 	if (ipmi_user_iter(ihp, ipmi_user_callback, &cb) != 0)
3151af98250Seschrock 		return (NULL);
3161af98250Seschrock 
3171af98250Seschrock 	if (cb.uic_result == NULL)
3181af98250Seschrock 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT,
3191af98250Seschrock 		    "no such user");
3201af98250Seschrock 
3211af98250Seschrock 	return (cb.uic_result);
3221af98250Seschrock }
3231af98250Seschrock 
3241af98250Seschrock int
ipmi_user_set_password(ipmi_handle_t * ihp,uint8_t uid,const char * passwd)3251af98250Seschrock ipmi_user_set_password(ipmi_handle_t *ihp, uint8_t uid, const char *passwd)
3261af98250Seschrock {
3271af98250Seschrock 	ipmi_set_user_password_t req = { 0 };
3281af98250Seschrock 	ipmi_cmd_t cmd;
3291af98250Seschrock 
3301af98250Seschrock 	req.isup_uid = uid;
3311af98250Seschrock 	req.isup_op = IPMI_PASSWORD_OP_SET;
3321af98250Seschrock 
3331af98250Seschrock 	if (strlen(passwd) > 19)
3341af98250Seschrock 		return (ipmi_set_error(ihp, EIPMI_INVALID_REQUEST,
3351af98250Seschrock 		    "password length must be less than 20 characters"));
3361af98250Seschrock 
3371af98250Seschrock 	if (strlen(passwd) > 15)
3381af98250Seschrock 		req.isup_len20 = 1;
3391af98250Seschrock 
3401af98250Seschrock 	(void) strcpy(req.isup_passwd, passwd);
3411af98250Seschrock 
3421af98250Seschrock 	cmd.ic_netfn = IPMI_NETFN_APP;
3431af98250Seschrock 	cmd.ic_cmd = IPMI_CMD_SET_USER_PASSWORD;
3441af98250Seschrock 	cmd.ic_lun = 0;
3451af98250Seschrock 	cmd.ic_data = &req;
3461af98250Seschrock 	if (req.isup_len20)
3471af98250Seschrock 		cmd.ic_dlen = sizeof (req);
3481af98250Seschrock 	else
3491af98250Seschrock 		cmd.ic_dlen = sizeof (req) - 4;
3501af98250Seschrock 
3511af98250Seschrock 	if (ipmi_send(ihp, &cmd) == NULL)
3521af98250Seschrock 		return (-1);
3531af98250Seschrock 
3541af98250Seschrock 	return (0);
3551af98250Seschrock }
356