blob: 507d95c8bb26458df293692d12464ff5e081b73e [file] [log] [blame]
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +05301/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
17#include "usercommands.hpp"
18
19#include "apphandler.hpp"
Richard Marian Thomaiyar06df8762018-12-08 17:38:25 +053020#include "channel_layer.hpp"
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053021#include "user_layer.hpp"
22
23#include <host-ipmid/ipmid-api.h>
24#include <security/pam_appl.h>
25
26#include <phosphor-logging/log.hpp>
27#include <regex>
28
29namespace ipmi
30{
31
32using namespace phosphor::logging;
33
34static constexpr uint8_t maxIpmi20PasswordSize = 20;
35static constexpr uint8_t maxIpmi15PasswordSize = 16;
36static constexpr uint8_t disableUser = 0x00;
37static constexpr uint8_t enableUser = 0x01;
38static constexpr uint8_t setPassword = 0x02;
39static constexpr uint8_t testPassword = 0x03;
Richard Marian Thomaiyar23df06f2019-01-04 16:42:22 +053040static constexpr uint8_t passwordKeySize20 = 1;
41static constexpr uint8_t passwordKeySize16 = 0;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053042
Richard Marian Thomaiyar6e1ba9e2018-11-29 06:29:21 +053043/** @struct SetUserAccessReq
44 *
45 * Structure for set user access request command (refer spec sec 22.26)
46 */
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053047struct SetUserAccessReq
48{
49#if BYTE_ORDER == LITTLE_ENDIAN
50 uint8_t chNum : 4;
51 uint8_t ipmiEnabled : 1;
52 uint8_t linkAuthEnabled : 1;
53 uint8_t accessCallback : 1;
54 uint8_t bitsUpdate : 1;
55 uint8_t userId : 6;
56 uint8_t reserved1 : 2;
57 uint8_t privilege : 4;
58 uint8_t reserved2 : 4;
59 uint8_t sessLimit : 4; // optional byte 4
60 uint8_t reserved3 : 4;
61#endif
62#if BYTE_ORDER == BIG_ENDIAN
63 uint8_t bitsUpdate : 1;
64 uint8_t accessCallback : 1;
65 uint8_t linkAuthEnabled : 1;
66 uint8_t ipmiEnabled : 1;
67 uint8_t chNum : 4;
68 uint8_t reserved1 : 2;
69 uint8_t userId : 6;
70 uint8_t reserved2 : 4;
71 uint8_t privilege : 4;
72 uint8_t reserved3 : 4;
73 uint8_t sessLimit : 4; // optional byte 4
74#endif
75
76} __attribute__((packed));
77
Richard Marian Thomaiyar6e1ba9e2018-11-29 06:29:21 +053078/** @struct GetUserAccessReq
79 *
80 * Structure for get user access request command (refer spec sec 22.27)
81 */
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +053082struct GetUserAccessReq
83{
84#if BYTE_ORDER == LITTLE_ENDIAN
85 uint8_t chNum : 4;
86 uint8_t reserved1 : 4;
87 uint8_t userId : 6;
88 uint8_t reserved2 : 2;
89#endif
90#if BYTE_ORDER == BIG_ENDIAN
91 uint8_t reserved1 : 4;
92 uint8_t chNum : 4;
93 uint8_t reserved2 : 2;
94 uint8_t userId : 6;
95#endif
96} __attribute__((packed));
97
Richard Marian Thomaiyar6e1ba9e2018-11-29 06:29:21 +053098/** @struct GetUserAccessResp
99 *
100 * Structure for get user access response command (refer spec sec 22.27)
101 */
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530102struct GetUserAccessResp
103{
104#if BYTE_ORDER == LITTLE_ENDIAN
105 uint8_t maxChUsers : 6;
106 uint8_t reserved1 : 2;
107 uint8_t enabledUsers : 6;
108 uint8_t enabledStatus : 2;
109 uint8_t fixedUsers : 6;
110 uint8_t reserved2 : 2;
111#endif
112#if BYTE_ORDER == BIG_ENDIAN
113 uint8_t reserved1 : 2;
114 uint8_t maxChUsers : 6;
115 uint8_t enabledStatus : 2;
116 uint8_t enabledUsers : 6;
117 uint8_t reserved2 : 2;
118 uint8_t fixedUsers : 6;
119#endif
120 PrivAccess privAccess;
121} __attribute__((packed));
122
Richard Marian Thomaiyar6e1ba9e2018-11-29 06:29:21 +0530123/** @struct SetUserNameReq
124 *
125 * Structure for set user name request command (refer spec sec 22.28)
126 */
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530127struct SetUserNameReq
128{
129#if BYTE_ORDER == LITTLE_ENDIAN
130 uint8_t userId : 6;
131 uint8_t reserved1 : 2;
132#endif
133#if BYTE_ORDER == BIG_ENDIAN
134 uint8_t reserved1 : 2;
135 uint8_t userId : 6;
136#endif
137 uint8_t userName[16];
138} __attribute__((packed));
139
Richard Marian Thomaiyar6e1ba9e2018-11-29 06:29:21 +0530140/** @struct GetUserNameReq
141 *
142 * Structure for get user name request command (refer spec sec 22.29)
143 */
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530144struct GetUserNameReq
145{
146#if BYTE_ORDER == LITTLE_ENDIAN
147 uint8_t userId : 6;
148 uint8_t reserved1 : 2;
149#endif
150#if BYTE_ORDER == BIG_ENDIAN
151 uint8_t reserved1 : 2;
152 uint8_t userId : 6;
153#endif
154} __attribute__((packed));
155
Richard Marian Thomaiyar6e1ba9e2018-11-29 06:29:21 +0530156/** @struct GetUserNameResp
157 *
158 * Structure for get user name response command (refer spec sec 22.29)
159 */
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530160struct GetUserNameResp
161{
162 uint8_t userName[16];
163} __attribute__((packed));
164
Richard Marian Thomaiyar6e1ba9e2018-11-29 06:29:21 +0530165/** @struct SetUserPasswordReq
166 *
167 * Structure for set user password request command (refer spec sec 22.30)
168 */
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530169struct SetUserPasswordReq
170{
171#if BYTE_ORDER == LITTLE_ENDIAN
172 uint8_t userId : 6;
173 uint8_t reserved1 : 1;
174 uint8_t ipmi20 : 1;
175 uint8_t operation : 2;
176 uint8_t reserved2 : 6;
177#endif
178#if BYTE_ORDER == BIG_ENDIAN
179 uint8_t ipmi20 : 1;
180 uint8_t reserved1 : 1;
181 uint8_t userId : 6;
182 uint8_t reserved2 : 6;
183 uint8_t operation : 2;
184#endif
185 uint8_t userPassword[maxIpmi20PasswordSize];
186} __attribute__((packed));
187
188ipmi_ret_t ipmiSetUserAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
189 ipmi_request_t request, ipmi_response_t response,
190 ipmi_data_len_t dataLen, ipmi_context_t context)
191{
192 const SetUserAccessReq* req = static_cast<SetUserAccessReq*>(request);
193 size_t reqLength = *dataLen;
Richard Marian Thomaiyar4026e442018-11-30 16:44:15 +0530194 *dataLen = 0;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530195
196 if (!(reqLength == sizeof(*req) ||
197 (reqLength == (sizeof(*req) - sizeof(uint8_t) /* skip optional*/))))
198 {
199 log<level::DEBUG>("Set user access - Invalid Length");
200 return IPMI_CC_REQ_DATA_LEN_INVALID;
201 }
Richard Marian Thomaiyar06df8762018-12-08 17:38:25 +0530202 uint8_t chNum = convertCurrentChannelNum(req->chNum);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530203 if (req->reserved1 != 0 || req->reserved2 != 0 || req->reserved3 != 0 ||
Richard Marian Thomaiyar06df8762018-12-08 17:38:25 +0530204 req->sessLimit != 0 || (!isValidChannel(chNum)) ||
205 (!ipmiUserIsValidPrivilege(req->privilege)) ||
206 (EChannelSessSupported::none == getChannelSessionSupport(chNum)))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530207 {
208 log<level::DEBUG>("Set user access - Invalid field in request");
209 return IPMI_CC_INVALID_FIELD_REQUEST;
210 }
211 if (!ipmiUserIsValidUserId(req->userId))
212 {
213 log<level::DEBUG>("Set user access - Parameter out of range");
214 return IPMI_CC_PARM_OUT_OF_RANGE;
215 }
Richard Marian Thomaiyar06df8762018-12-08 17:38:25 +0530216
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530217 PrivAccess privAccess = {0};
218 if (req->bitsUpdate)
219 {
220 privAccess.ipmiEnabled = req->ipmiEnabled;
221 privAccess.linkAuthEnabled = req->linkAuthEnabled;
222 privAccess.accessCallback = req->accessCallback;
223 }
224 privAccess.privilege = req->privilege;
Richard Marian Thomaiyarb6771e02019-01-29 14:42:44 +0530225 return ipmiUserSetPrivilegeAccess(req->userId, chNum, privAccess,
226 req->bitsUpdate);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530227}
228
229ipmi_ret_t ipmiGetUserAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
230 ipmi_request_t request, ipmi_response_t response,
231 ipmi_data_len_t dataLen, ipmi_context_t context)
232{
233 const GetUserAccessReq* req = static_cast<GetUserAccessReq*>(request);
234 size_t reqLength = *dataLen;
Richard Marian Thomaiyarb6771e02019-01-29 14:42:44 +0530235 ipmi_ret_t retStatus = IPMI_CC_OK;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530236
237 *dataLen = 0;
238
239 if (reqLength != sizeof(*req))
240 {
241 log<level::DEBUG>("Get user access - Invalid Length");
242 return IPMI_CC_REQ_DATA_LEN_INVALID;
243 }
Richard Marian Thomaiyar06df8762018-12-08 17:38:25 +0530244 uint8_t chNum = convertCurrentChannelNum(req->chNum);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530245 if (req->reserved1 != 0 || req->reserved2 != 0 ||
Richard Marian Thomaiyar06df8762018-12-08 17:38:25 +0530246 (!isValidChannel(chNum)) ||
247 (EChannelSessSupported::none == getChannelSessionSupport(chNum)))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530248 {
249 log<level::DEBUG>("Get user access - Invalid field in request");
250 return IPMI_CC_INVALID_FIELD_REQUEST;
251 }
252 if (!ipmiUserIsValidUserId(req->userId))
253 {
254 log<level::DEBUG>("Get user access - Parameter out of range");
255 return IPMI_CC_PARM_OUT_OF_RANGE;
256 }
257
258 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
259 bool enabledState = false;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530260 GetUserAccessResp* resp = static_cast<GetUserAccessResp*>(response);
261
262 std::fill(reinterpret_cast<uint8_t*>(resp),
263 reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
264
Richard Marian Thomaiyarb6771e02019-01-29 14:42:44 +0530265 retStatus = ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers);
266 if (retStatus != IPMI_CC_OK)
267 {
268 return retStatus;
269 }
270
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530271 resp->maxChUsers = maxChUsers;
272 resp->enabledUsers = enabledUsers;
273 resp->fixedUsers = fixedUsers;
274
Richard Marian Thomaiyarb6771e02019-01-29 14:42:44 +0530275 retStatus = ipmiUserCheckEnabled(req->userId, enabledState);
276 if (retStatus != IPMI_CC_OK)
277 {
278 return retStatus;
279 }
280
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530281 resp->enabledStatus = enabledState ? userIdEnabledViaSetPassword
282 : userIdDisabledViaSetPassword;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530283 *dataLen = sizeof(*resp);
Richard Marian Thomaiyarb6771e02019-01-29 14:42:44 +0530284 return ipmiUserGetPrivilegeAccess(req->userId, chNum, resp->privAccess);
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530285}
286
287ipmi_ret_t ipmiSetUserName(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
288 ipmi_request_t request, ipmi_response_t response,
289 ipmi_data_len_t dataLen, ipmi_context_t context)
290{
291 const SetUserNameReq* req = static_cast<SetUserNameReq*>(request);
292 size_t reqLength = *dataLen;
293 *dataLen = 0;
294
295 if (reqLength != sizeof(*req))
296 {
297 log<level::DEBUG>("Set user name - Invalid Length");
298 return IPMI_CC_REQ_DATA_LEN_INVALID;
299 }
300 if (req->reserved1)
301 {
302 return IPMI_CC_INVALID_FIELD_REQUEST;
303 }
304 if (!ipmiUserIsValidUserId(req->userId))
305 {
306 log<level::DEBUG>("Set user name - Invalid user id");
307 return IPMI_CC_PARM_OUT_OF_RANGE;
308 }
309
310 return ipmiUserSetUserName(req->userId,
311 reinterpret_cast<const char*>(req->userName));
312}
313
314/** @brief implementes the get user name command
315 * @param[in] netfn - specifies netfn.
316 * @param[in] cmd - specifies cmd number.
317 * @param[in] request - pointer to request data.
318 * @param[in, out] dataLen - specifies request data length, and returns
319 * response data length.
320 * @param[in] context - ipmi context.
321 * @returns ipmi completion code.
322 */
323ipmi_ret_t ipmiGetUserName(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
324 ipmi_request_t request, ipmi_response_t response,
325 ipmi_data_len_t dataLen, ipmi_context_t context)
326{
327 const GetUserNameReq* req = static_cast<GetUserNameReq*>(request);
328 size_t reqLength = *dataLen;
329
330 *dataLen = 0;
331
332 if (reqLength != sizeof(*req))
333 {
334 log<level::DEBUG>("Get user name - Invalid Length");
335 return IPMI_CC_REQ_DATA_LEN_INVALID;
336 }
337
338 std::string userName;
339 if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK)
340 { // Invalid User ID
341 log<level::DEBUG>("User Name not found",
342 entry("USER-ID:%d", (uint8_t)req->userId));
343 return IPMI_CC_PARM_OUT_OF_RANGE;
344 }
345 GetUserNameResp* resp = static_cast<GetUserNameResp*>(response);
346 std::fill(reinterpret_cast<uint8_t*>(resp),
347 reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
348 userName.copy(reinterpret_cast<char*>(resp->userName),
349 sizeof(resp->userName), 0);
350 *dataLen = sizeof(*resp);
351
352 return IPMI_CC_OK;
353}
354
355int pamFunctionConversation(int numMsg, const struct pam_message** msg,
356 struct pam_response** resp, void* appdataPtr)
357{
358 if (appdataPtr == nullptr)
359 {
360 return PAM_AUTH_ERR;
361 }
362 size_t passSize = std::strlen(reinterpret_cast<char*>(appdataPtr)) + 1;
363 char* pass = reinterpret_cast<char*>(malloc(passSize));
364 std::strncpy(pass, reinterpret_cast<char*>(appdataPtr), passSize);
365
366 *resp = reinterpret_cast<pam_response*>(
367 calloc(numMsg, sizeof(struct pam_response)));
368
369 for (int i = 0; i < numMsg; ++i)
370 {
371 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
372 {
373 continue;
374 }
375 resp[i]->resp = pass;
376 }
377 return PAM_SUCCESS;
378}
379
380bool pamUpdatePasswd(const char* username, const char* password)
381{
382 const struct pam_conv localConversation = {pamFunctionConversation,
383 const_cast<char*>(password)};
384 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
385
386 if (pam_start("passwd", username, &localConversation, &localAuthHandle) !=
387 PAM_SUCCESS)
388 {
389 return false;
390 }
391 int retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
392
393 if (retval != PAM_SUCCESS)
394 {
395 if (retval == PAM_AUTHTOK_ERR)
396 {
397 log<level::DEBUG>("Authentication Failure");
398 }
399 else
400 {
401 log<level::DEBUG>("pam_chauthtok returned failure",
402 entry("ERROR=%d", retval));
403 }
404 pam_end(localAuthHandle, retval);
405 return false;
406 }
407 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
408 {
409 return false;
410 }
411 return true;
412}
413
414/** @brief implementes the set user password command
415 * @param[in] netfn - specifies netfn.
416 * @param[in] cmd - specifies cmd number.
417 * @param[in] request - pointer to request data.
418 * @param[in, out] dataLen - specifies request data length, and returns
419 * response data length.
420 * @param[in] context - ipmi context.
421 * @returns ipmi completion code.
422 */
423ipmi_ret_t ipmiSetUserPassword(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
424 ipmi_request_t request, ipmi_response_t response,
425 ipmi_data_len_t dataLen, ipmi_context_t context)
426{
427 const SetUserPasswordReq* req = static_cast<SetUserPasswordReq*>(request);
428 size_t reqLength = *dataLen;
429 // subtract 2 bytes header to know the password length - including NULL
430 uint8_t passwordLength = *dataLen - 2;
431 *dataLen = 0;
432
433 // verify input length based on operation. Required password size is 20
434 // bytes as we support only IPMI 2.0, but in order to be compatible with
435 // tools, accept 16 bytes of password size too.
436 if (reqLength < 2 ||
437 // If enable / disable user, reqLength has to be >=2 & <= 22
438 ((req->operation == disableUser || req->operation == enableUser) &&
439 ((reqLength < 2) || (reqLength > sizeof(SetUserPasswordReq)))))
440 {
441 log<level::DEBUG>("Invalid Length");
442 return IPMI_CC_REQ_DATA_LEN_INVALID;
443 }
444 // If set / test password then password length has to be 16 or 20 bytes
Richard Marian Thomaiyar23df06f2019-01-04 16:42:22 +0530445 // based on the password size bit.
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530446 if (((req->operation == setPassword) || (req->operation == testPassword)) &&
Richard Marian Thomaiyar23df06f2019-01-04 16:42:22 +0530447 (((req->ipmi20 == passwordKeySize20) &&
448 (passwordLength != maxIpmi20PasswordSize)) ||
449 ((req->ipmi20 == passwordKeySize16) &&
450 (passwordLength != maxIpmi15PasswordSize))))
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530451 {
452 log<level::DEBUG>("Invalid Length");
453 return IPMI_CC_REQ_DATA_LEN_INVALID;
454 }
455
456 std::string userName;
457 if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK)
458 {
459 log<level::DEBUG>("User Name not found",
460 entry("USER-ID:%d", (uint8_t)req->userId));
461 return IPMI_CC_PARM_OUT_OF_RANGE;
462 }
463 if (req->operation == setPassword)
464 {
465 std::string passwd;
466 passwd.assign(reinterpret_cast<const char*>(req->userPassword), 0,
467 maxIpmi20PasswordSize);
468 if (!std::regex_match(passwd.c_str(),
469 std::regex("[a-zA-z_0-9][a-zA-Z_0-9,?:`!\"]*")))
470 {
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530471 log<level::ERR>("Invalid password fields",
472 entry("USER-ID:%d", (uint8_t)req->userId));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530473 return IPMI_CC_INVALID_FIELD_REQUEST;
474 }
475 if (!pamUpdatePasswd(userName.c_str(), passwd.c_str()))
476 {
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530477 log<level::ERR>("Failed to update password",
478 entry("USER-ID:%d", (uint8_t)req->userId));
479 return IPMI_CC_INVALID_FIELD_REQUEST;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530480 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530481 return IPMI_CC_OK;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530482 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530483 else if (req->operation == enableUser || req->operation == disableUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530484 {
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530485 return ipmiUserUpdateEnabledState(req->userId,
486 static_cast<bool>(req->operation));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530487 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530488 else if (req->operation == testPassword)
489 {
490 auto password = ipmiUserGetPassword(userName);
491 std::string testPassword(
492 reinterpret_cast<const char*>(req->userPassword), 0,
493 passwordLength);
494 // Note: For security reasons password size won't be compared and
495 // wrong password size completion code will not be returned if size
496 // doesn't match as specified in IPMI specification.
497 if (password != testPassword)
498 {
499 log<level::DEBUG>("Test password failed",
500 entry("USER-ID:%d", (uint8_t)req->userId));
501 return static_cast<ipmi_ret_t>(
502 IPMISetPasswordReturnCodes::ipmiCCPasswdFailMismatch);
503 }
504 return IPMI_CC_OK;
505 }
506 return IPMI_CC_INVALID_FIELD_REQUEST;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530507}
508
William A. Kennington III343d0612018-12-10 15:56:24 -0800509void registerUserIpmiFunctions() __attribute__((constructor));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530510void registerUserIpmiFunctions()
511{
512 ipmiUserInit();
513 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_ACCESS, NULL,
514 ipmiSetUserAccess, PRIVILEGE_ADMIN);
515
516 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_USER_ACCESS, NULL,
517 ipmiGetUserAccess, PRIVILEGE_OPERATOR);
518
519 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_USER_NAME, NULL,
520 ipmiGetUserName, PRIVILEGE_OPERATOR);
521
522 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_NAME, NULL,
523 ipmiSetUserName, PRIVILEGE_ADMIN);
524
525 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_PASSWORD, NULL,
526 ipmiSetUserPassword, PRIVILEGE_ADMIN);
527
528 return;
529}
530} // namespace ipmi