15345Seschrock /*
25345Seschrock  * CDDL HEADER START
35345Seschrock  *
45345Seschrock  * The contents of this file are subject to the terms of the
55345Seschrock  * Common Development and Distribution License (the "License").
65345Seschrock  * You may not use this file except in compliance with the License.
75345Seschrock  *
85345Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95345Seschrock  * or http://www.opensolaris.org/os/licensing.
105345Seschrock  * See the License for the specific language governing permissions
115345Seschrock  * and limitations under the License.
125345Seschrock  *
135345Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
145345Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155345Seschrock  * If applicable, add the following below this CDDL HEADER, with the
165345Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
175345Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
185345Seschrock  *
195345Seschrock  * CDDL HEADER END
205345Seschrock  */
215345Seschrock /*
225345Seschrock  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
235345Seschrock  * Use is subject to license terms.
245345Seschrock  */
255345Seschrock 
265345Seschrock #pragma ident	"%Z%%M%	%I%	%E% SMI"
275345Seschrock 
285345Seschrock #include <libipmi.h>
295345Seschrock #include <string.h>
305345Seschrock 
315345Seschrock #include "ipmi_impl.h"
325345Seschrock 
335345Seschrock /*
345345Seschrock  * Get User Access.  See section 22.27.
355345Seschrock  *
365345Seschrock  * See libipmi.h for a complete description of IPMI reference material.
375345Seschrock  */
385345Seschrock 
395345Seschrock typedef struct ipmi_get_user_access_req {
40*5621Seschrock 	DECL_BITFIELD2(
41*5621Seschrock 	    igua_channel		:4,
42*5621Seschrock 	    __reserved1			:4);
43*5621Seschrock 	DECL_BITFIELD2(
44*5621Seschrock 	    igua_uid			:2,
45*5621Seschrock 	    __reserved2			:6);
465345Seschrock } ipmi_get_user_access_req_t;
475345Seschrock 
485345Seschrock #define	IPMI_CMD_GET_USER_ACCESS	0x44
495345Seschrock 
505345Seschrock typedef struct ipmi_get_user_access {
51*5621Seschrock 	DECL_BITFIELD2(
52*5621Seschrock 	    igua_max_uid		:4,
53*5621Seschrock 	    __reserved1			:4);
54*5621Seschrock 	DECL_BITFIELD2(
55*5621Seschrock 	    igua_enable_status		:4,
56*5621Seschrock 	    igua_enabled_uid		:4);
57*5621Seschrock 	DECL_BITFIELD2(
58*5621Seschrock 	    __reserved2			:4,
59*5621Seschrock 	    igua_fixed_uid		:4);
60*5621Seschrock 	DECL_BITFIELD5(
61*5621Seschrock 	    __reserved3			:1,
62*5621Seschrock 	    igua_only_callback		:1,
63*5621Seschrock 	    igua_link_auth_enable	:1,
64*5621Seschrock 	    igua_ipmi_msg_enable	:1,
65*5621Seschrock 	    igua_privilege_level	:4);
665345Seschrock } ipmi_get_user_access_t;
675345Seschrock 
685345Seschrock #define	IPMI_USER_ENABLE_UNSPECIFIED	0x00
695345Seschrock #define	IPMI_USER_ENABLE_SETPASSWD	0x01
705345Seschrock #define	IPMI_USER_DISABLE_SETPASSWD	0x02
715345Seschrock 
725345Seschrock #define	IPMI_USER_CHANNEL_CURRENT	0xe
735345Seschrock 
745345Seschrock /*
755345Seschrock  * Get User Name.  See section 22.29
765345Seschrock  */
775345Seschrock 
785345Seschrock #define	IPMI_CMD_GET_USER_NAME		0x46
795345Seschrock 
805345Seschrock /*
815345Seschrock  * Set User Password.  See section 22.30
825345Seschrock  */
835345Seschrock 
845345Seschrock #define	IPMI_CMD_SET_USER_PASSWORD	0x47
855345Seschrock 
865345Seschrock typedef struct ipmi_set_user_password {
87*5621Seschrock 	DECL_BITFIELD3(
88*5621Seschrock 	    isup_uid		:6,
89*5621Seschrock 	    __reserved1		:1,
90*5621Seschrock 	    isup_len20		:1);
91*5621Seschrock 	DECL_BITFIELD2(
92*5621Seschrock 	    isup_op		:2,
93*5621Seschrock 	    __reserved2		:6);
945345Seschrock 	char		isup_passwd[20];
955345Seschrock } ipmi_set_user_password_t;
965345Seschrock 
975345Seschrock #define	IPMI_PASSWORD_OP_DISABLE	0x0
985345Seschrock #define	IPMI_PASSWORD_OP_ENABLE		0x1
995345Seschrock #define	IPMI_PASSWORD_OP_SET		0x2
1005345Seschrock #define	IPMI_PASSWORD_OP_TEST		0x3
1015345Seschrock 
1025345Seschrock static ipmi_get_user_access_t *
1035345Seschrock ipmi_get_user_access(ipmi_handle_t *ihp, uint8_t channel, uint8_t uid)
1045345Seschrock {
1055345Seschrock 	ipmi_cmd_t cmd, *resp;
1065345Seschrock 	ipmi_get_user_access_req_t req = { 0 };
1075345Seschrock 
1085345Seschrock 	req.igua_channel = channel;
1095345Seschrock 	req.igua_uid = uid;
1105345Seschrock 
1115345Seschrock 	cmd.ic_netfn = IPMI_NETFN_APP;
1125345Seschrock 	cmd.ic_cmd = IPMI_CMD_GET_USER_ACCESS;
1135345Seschrock 	cmd.ic_lun = 0;
1145345Seschrock 	cmd.ic_data = &req;
1155345Seschrock 	cmd.ic_dlen = sizeof (req);
1165345Seschrock 
1175345Seschrock 	if ((resp = ipmi_send(ihp, &cmd)) == NULL) {
1185345Seschrock 		/*
1195345Seschrock 		 * If sessions aren't supported on the current channel, some
1205345Seschrock 		 * service processors (notably Sun's ILOM) will return an
1215345Seschrock 		 * invalid request completion code (0xCC).  For these SPs, we
1225345Seschrock 		 * translate this to the more appropriate EIPMI_INVALID_COMMAND.
1235345Seschrock 		 */
1245345Seschrock 		if (ipmi_errno(ihp) == EIPMI_INVALID_REQUEST)
1255345Seschrock 			(void) ipmi_set_error(ihp, EIPMI_INVALID_COMMAND,
1265345Seschrock 			    NULL);
1275345Seschrock 		return (NULL);
1285345Seschrock 	}
1295345Seschrock 
1305345Seschrock 	if (resp->ic_dlen < sizeof (ipmi_get_user_access_t)) {
1315345Seschrock 		(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
1325345Seschrock 		return (NULL);
1335345Seschrock 	}
1345345Seschrock 
1355345Seschrock 	return (resp->ic_data);
1365345Seschrock }
1375345Seschrock 
1385345Seschrock static const char *
1395345Seschrock ipmi_get_user_name(ipmi_handle_t *ihp, uint8_t uid)
1405345Seschrock {
1415345Seschrock 	ipmi_cmd_t cmd, *resp;
1425345Seschrock 
1435345Seschrock 	cmd.ic_netfn = IPMI_NETFN_APP;
1445345Seschrock 	cmd.ic_cmd = IPMI_CMD_GET_USER_ACCESS;
1455345Seschrock 	cmd.ic_lun = 0;
1465345Seschrock 	cmd.ic_data = &uid;
1475345Seschrock 	cmd.ic_dlen = sizeof (uid);
1485345Seschrock 
1495345Seschrock 	if ((resp = ipmi_send(ihp, &cmd)) == NULL)
1505345Seschrock 		return (NULL);
1515345Seschrock 
1525345Seschrock 	if (resp->ic_dlen < 16) {
1535345Seschrock 		(void) ipmi_set_error(ihp, EIPMI_BAD_RESPONSE_LENGTH, NULL);
1545345Seschrock 		return (NULL);
1555345Seschrock 	}
1565345Seschrock 
1575345Seschrock 	return (resp->ic_data);
1585345Seschrock }
1595345Seschrock 
1605345Seschrock void
1615345Seschrock ipmi_user_clear(ipmi_handle_t *ihp)
1625345Seschrock {
1635345Seschrock 	ipmi_user_t *up, *next;
1645345Seschrock 
1655345Seschrock 	while ((up = ihp->ih_users) != NULL) {
1665345Seschrock 		next = up->iu_next;
1675345Seschrock 		ipmi_free(ihp, up->iu_name);
1685345Seschrock 		ipmi_free(ihp, up);
1695345Seschrock 		ihp->ih_users = next;
1705345Seschrock 	}
1715345Seschrock }
1725345Seschrock 
1735345Seschrock /*
1745345Seschrock  * Returns user information in a well-defined structure.
1755345Seschrock  */
1765345Seschrock int
1775345Seschrock ipmi_user_iter(ipmi_handle_t *ihp, int (*func)(ipmi_user_t *, void *),
1785345Seschrock     void *data)
1795345Seschrock {
1805345Seschrock 	ipmi_get_user_access_t *resp;
1815345Seschrock 	uint8_t i;
1825345Seschrock 	ipmi_user_t *up;
1835345Seschrock 	const char *name;
1845345Seschrock 
1855345Seschrock 	ipmi_user_clear(ihp);
1865345Seschrock 
1875345Seschrock 	/*
1885345Seschrock 	 * First get the number of active users on the system by requesting the
1895345Seschrock 	 * reserved user ID (0).
1905345Seschrock 	 */
1915345Seschrock 	if ((resp = ipmi_get_user_access(ihp,
1925345Seschrock 	    IPMI_USER_CHANNEL_CURRENT, 0)) == NULL)
1935345Seschrock 		return (-1);
1945345Seschrock 
1955345Seschrock 	for (i = 1; i <= resp->igua_max_uid; i++) {
1965345Seschrock 		if ((resp = ipmi_get_user_access(ihp,
1975345Seschrock 		    IPMI_USER_CHANNEL_CURRENT, i)) == NULL)
1985345Seschrock 			return (-1);
1995345Seschrock 
2005345Seschrock 		if ((up = ipmi_zalloc(ihp, sizeof (ipmi_user_t))) == NULL)
2015345Seschrock 			return (-1);
2025345Seschrock 
2035345Seschrock 		up->iu_enabled = resp->igua_enabled_uid;
2045345Seschrock 		up->iu_uid = i;
2055345Seschrock 		up->iu_ipmi_msg_enable = resp->igua_ipmi_msg_enable;
2065345Seschrock 		up->iu_link_auth_enable = resp->igua_link_auth_enable;
2075345Seschrock 		up->iu_priv = resp->igua_privilege_level;
2085345Seschrock 		up->iu_next = ihp->ih_users;
2095345Seschrock 		ihp->ih_users = up;
2105345Seschrock 
2115345Seschrock 		if ((name = ipmi_get_user_name(ihp, i)) == NULL)
2125345Seschrock 			return (-1);
2135345Seschrock 
2145345Seschrock 		if (*name != '\0' &&
2155345Seschrock 		    (up->iu_name = ipmi_strdup(ihp, name)) == NULL)
2165345Seschrock 			return (-1);
2175345Seschrock 	}
2185345Seschrock 
2195345Seschrock 	for (up = ihp->ih_users; up != NULL; up = up->iu_next) {
2205345Seschrock 		if (func(up, data) != 0)
2215345Seschrock 			return (-1);
2225345Seschrock 	}
2235345Seschrock 
2245345Seschrock 	return (0);
2255345Seschrock }
2265345Seschrock 
2275345Seschrock typedef struct ipmi_user_cb {
2285345Seschrock 	const char	*uic_name;
2295345Seschrock 	uint8_t		uic_uid;
2305345Seschrock 	ipmi_user_t	*uic_result;
2315345Seschrock } ipmi_user_cb_t;
2325345Seschrock 
2335345Seschrock static int
2345345Seschrock ipmi_user_callback(ipmi_user_t *up, void *data)
2355345Seschrock {
2365345Seschrock 	ipmi_user_cb_t *cbp = data;
2375345Seschrock 
2385345Seschrock 	if (cbp->uic_result != NULL)
2395345Seschrock 		return (0);
2405345Seschrock 
2415345Seschrock 	if (up->iu_name) {
2425345Seschrock 		if (strcmp(up->iu_name, cbp->uic_name) == 0)
2435345Seschrock 			cbp->uic_result = up;
2445345Seschrock 	} else if (up->iu_uid == cbp->uic_uid) {
2455345Seschrock 		cbp->uic_result = up;
2465345Seschrock 	}
2475345Seschrock 
2485345Seschrock 	return (0);
2495345Seschrock }
2505345Seschrock 
2515345Seschrock ipmi_user_t *
2525345Seschrock ipmi_user_lookup_name(ipmi_handle_t *ihp, const char *name)
2535345Seschrock {
2545345Seschrock 	ipmi_user_cb_t cb = { 0 };
2555345Seschrock 
2565345Seschrock 	cb.uic_name = name;
2575345Seschrock 	cb.uic_result = NULL;
2585345Seschrock 
2595345Seschrock 	if (ipmi_user_iter(ihp, ipmi_user_callback, &cb) != 0)
2605345Seschrock 		return (NULL);
2615345Seschrock 
2625345Seschrock 	if (cb.uic_result == NULL)
2635345Seschrock 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT,
2645345Seschrock 		    "no such user");
2655345Seschrock 
2665345Seschrock 	return (cb.uic_result);
2675345Seschrock }
2685345Seschrock 
2695345Seschrock ipmi_user_t *
2705345Seschrock ipmi_user_lookup_id(ipmi_handle_t *ihp, uint8_t uid)
2715345Seschrock {
2725345Seschrock 	ipmi_user_cb_t cb = { 0 };
2735345Seschrock 
2745345Seschrock 	cb.uic_uid = uid;
2755345Seschrock 	cb.uic_result = NULL;
2765345Seschrock 
2775345Seschrock 	if (ipmi_user_iter(ihp, ipmi_user_callback, &cb) != 0)
2785345Seschrock 		return (NULL);
2795345Seschrock 
2805345Seschrock 	if (cb.uic_result == NULL)
2815345Seschrock 		(void) ipmi_set_error(ihp, EIPMI_NOT_PRESENT,
2825345Seschrock 		    "no such user");
2835345Seschrock 
2845345Seschrock 	return (cb.uic_result);
2855345Seschrock }
2865345Seschrock 
2875345Seschrock int
2885345Seschrock ipmi_user_set_password(ipmi_handle_t *ihp, uint8_t uid, const char *passwd)
2895345Seschrock {
2905345Seschrock 	ipmi_set_user_password_t req = { 0 };
2915345Seschrock 	ipmi_cmd_t cmd;
2925345Seschrock 
2935345Seschrock 	req.isup_uid = uid;
2945345Seschrock 	req.isup_op = IPMI_PASSWORD_OP_SET;
2955345Seschrock 
2965345Seschrock 	if (strlen(passwd) > 19)
2975345Seschrock 		return (ipmi_set_error(ihp, EIPMI_INVALID_REQUEST,
2985345Seschrock 		    "password length must be less than 20 characters"));
2995345Seschrock 
3005345Seschrock 	if (strlen(passwd) > 15)
3015345Seschrock 		req.isup_len20 = 1;
3025345Seschrock 
3035345Seschrock 	(void) strcpy(req.isup_passwd, passwd);
3045345Seschrock 
3055345Seschrock 	cmd.ic_netfn = IPMI_NETFN_APP;
3065345Seschrock 	cmd.ic_cmd = IPMI_CMD_SET_USER_PASSWORD;
3075345Seschrock 	cmd.ic_lun = 0;
3085345Seschrock 	cmd.ic_data = &req;
3095345Seschrock 	if (req.isup_len20)
3105345Seschrock 		cmd.ic_dlen = sizeof (req);
3115345Seschrock 	else
3125345Seschrock 		cmd.ic_dlen = sizeof (req) - 4;
3135345Seschrock 
3145345Seschrock 	if (ipmi_send(ihp, &cmd) == NULL)
3155345Seschrock 		return (-1);
3165345Seschrock 
3175345Seschrock 	return (0);
3185345Seschrock }
319