blob: f81c0931b2edff03e298e80d1e0a2785b20a299e [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"
20#include "user_layer.hpp"
21
22#include <host-ipmid/ipmid-api.h>
23#include <security/pam_appl.h>
24
25#include <phosphor-logging/log.hpp>
26#include <regex>
27
28namespace ipmi
29{
30
31using namespace phosphor::logging;
32
33static constexpr uint8_t maxIpmi20PasswordSize = 20;
34static constexpr uint8_t maxIpmi15PasswordSize = 16;
35static constexpr uint8_t disableUser = 0x00;
36static constexpr uint8_t enableUser = 0x01;
37static constexpr uint8_t setPassword = 0x02;
38static constexpr uint8_t testPassword = 0x03;
39
40struct SetUserAccessReq
41{
42#if BYTE_ORDER == LITTLE_ENDIAN
43 uint8_t chNum : 4;
44 uint8_t ipmiEnabled : 1;
45 uint8_t linkAuthEnabled : 1;
46 uint8_t accessCallback : 1;
47 uint8_t bitsUpdate : 1;
48 uint8_t userId : 6;
49 uint8_t reserved1 : 2;
50 uint8_t privilege : 4;
51 uint8_t reserved2 : 4;
52 uint8_t sessLimit : 4; // optional byte 4
53 uint8_t reserved3 : 4;
54#endif
55#if BYTE_ORDER == BIG_ENDIAN
56 uint8_t bitsUpdate : 1;
57 uint8_t accessCallback : 1;
58 uint8_t linkAuthEnabled : 1;
59 uint8_t ipmiEnabled : 1;
60 uint8_t chNum : 4;
61 uint8_t reserved1 : 2;
62 uint8_t userId : 6;
63 uint8_t reserved2 : 4;
64 uint8_t privilege : 4;
65 uint8_t reserved3 : 4;
66 uint8_t sessLimit : 4; // optional byte 4
67#endif
68
69} __attribute__((packed));
70
71struct GetUserAccessReq
72{
73#if BYTE_ORDER == LITTLE_ENDIAN
74 uint8_t chNum : 4;
75 uint8_t reserved1 : 4;
76 uint8_t userId : 6;
77 uint8_t reserved2 : 2;
78#endif
79#if BYTE_ORDER == BIG_ENDIAN
80 uint8_t reserved1 : 4;
81 uint8_t chNum : 4;
82 uint8_t reserved2 : 2;
83 uint8_t userId : 6;
84#endif
85} __attribute__((packed));
86
87struct GetUserAccessResp
88{
89#if BYTE_ORDER == LITTLE_ENDIAN
90 uint8_t maxChUsers : 6;
91 uint8_t reserved1 : 2;
92 uint8_t enabledUsers : 6;
93 uint8_t enabledStatus : 2;
94 uint8_t fixedUsers : 6;
95 uint8_t reserved2 : 2;
96#endif
97#if BYTE_ORDER == BIG_ENDIAN
98 uint8_t reserved1 : 2;
99 uint8_t maxChUsers : 6;
100 uint8_t enabledStatus : 2;
101 uint8_t enabledUsers : 6;
102 uint8_t reserved2 : 2;
103 uint8_t fixedUsers : 6;
104#endif
105 PrivAccess privAccess;
106} __attribute__((packed));
107
108struct SetUserNameReq
109{
110#if BYTE_ORDER == LITTLE_ENDIAN
111 uint8_t userId : 6;
112 uint8_t reserved1 : 2;
113#endif
114#if BYTE_ORDER == BIG_ENDIAN
115 uint8_t reserved1 : 2;
116 uint8_t userId : 6;
117#endif
118 uint8_t userName[16];
119} __attribute__((packed));
120
121struct GetUserNameReq
122{
123#if BYTE_ORDER == LITTLE_ENDIAN
124 uint8_t userId : 6;
125 uint8_t reserved1 : 2;
126#endif
127#if BYTE_ORDER == BIG_ENDIAN
128 uint8_t reserved1 : 2;
129 uint8_t userId : 6;
130#endif
131} __attribute__((packed));
132
133struct GetUserNameResp
134{
135 uint8_t userName[16];
136} __attribute__((packed));
137
138struct SetUserPasswordReq
139{
140#if BYTE_ORDER == LITTLE_ENDIAN
141 uint8_t userId : 6;
142 uint8_t reserved1 : 1;
143 uint8_t ipmi20 : 1;
144 uint8_t operation : 2;
145 uint8_t reserved2 : 6;
146#endif
147#if BYTE_ORDER == BIG_ENDIAN
148 uint8_t ipmi20 : 1;
149 uint8_t reserved1 : 1;
150 uint8_t userId : 6;
151 uint8_t reserved2 : 6;
152 uint8_t operation : 2;
153#endif
154 uint8_t userPassword[maxIpmi20PasswordSize];
155} __attribute__((packed));
156
157ipmi_ret_t ipmiSetUserAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
158 ipmi_request_t request, ipmi_response_t response,
159 ipmi_data_len_t dataLen, ipmi_context_t context)
160{
161 const SetUserAccessReq* req = static_cast<SetUserAccessReq*>(request);
162 size_t reqLength = *dataLen;
Richard Marian Thomaiyar4026e442018-11-30 16:44:15 +0530163 *dataLen = 0;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530164
165 if (!(reqLength == sizeof(*req) ||
166 (reqLength == (sizeof(*req) - sizeof(uint8_t) /* skip optional*/))))
167 {
168 log<level::DEBUG>("Set user access - Invalid Length");
169 return IPMI_CC_REQ_DATA_LEN_INVALID;
170 }
171 if (req->reserved1 != 0 || req->reserved2 != 0 || req->reserved3 != 0 ||
172 req->sessLimit != 0 ||
173 (!ipmiUserIsValidChannel(req->chNum) ||
174 (!ipmiUserIsValidPrivilege(req->privilege))))
175 // TODO: Need to check for session support and return invalid field in
176 // request
177 {
178 log<level::DEBUG>("Set user access - Invalid field in request");
179 return IPMI_CC_INVALID_FIELD_REQUEST;
180 }
181 if (!ipmiUserIsValidUserId(req->userId))
182 {
183 log<level::DEBUG>("Set user access - Parameter out of range");
184 return IPMI_CC_PARM_OUT_OF_RANGE;
185 }
186 // TODO: Determine the Channel number 0xE (Self Channel number ?)
187 uint8_t chNum = req->chNum;
188 PrivAccess privAccess = {0};
189 if (req->bitsUpdate)
190 {
191 privAccess.ipmiEnabled = req->ipmiEnabled;
192 privAccess.linkAuthEnabled = req->linkAuthEnabled;
193 privAccess.accessCallback = req->accessCallback;
194 }
195 privAccess.privilege = req->privilege;
196 ipmiUserSetPrivilegeAccess(req->userId, chNum, privAccess, req->bitsUpdate);
197
198 return IPMI_CC_OK;
199}
200
201ipmi_ret_t ipmiGetUserAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
202 ipmi_request_t request, ipmi_response_t response,
203 ipmi_data_len_t dataLen, ipmi_context_t context)
204{
205 const GetUserAccessReq* req = static_cast<GetUserAccessReq*>(request);
206 size_t reqLength = *dataLen;
207
208 *dataLen = 0;
209
210 if (reqLength != sizeof(*req))
211 {
212 log<level::DEBUG>("Get user access - Invalid Length");
213 return IPMI_CC_REQ_DATA_LEN_INVALID;
214 }
215 if (req->reserved1 != 0 || req->reserved2 != 0 ||
216 (!ipmiUserIsValidChannel(req->chNum)))
217 // TODO: Need to check for session support and return invalid field in
218 // request
219 {
220 log<level::DEBUG>("Get user access - Invalid field in request");
221 return IPMI_CC_INVALID_FIELD_REQUEST;
222 }
223 if (!ipmiUserIsValidUserId(req->userId))
224 {
225 log<level::DEBUG>("Get user access - Parameter out of range");
226 return IPMI_CC_PARM_OUT_OF_RANGE;
227 }
228
229 uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
230 bool enabledState = false;
231 // TODO: Determine the Channel number 0xE (Self Channel number ?)
232 uint8_t chNum = req->chNum;
233 GetUserAccessResp* resp = static_cast<GetUserAccessResp*>(response);
234
235 std::fill(reinterpret_cast<uint8_t*>(resp),
236 reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
237
238 ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers);
239 resp->maxChUsers = maxChUsers;
240 resp->enabledUsers = enabledUsers;
241 resp->fixedUsers = fixedUsers;
242
243 ipmiUserCheckEnabled(req->userId, enabledState);
244 resp->enabledStatus = enabledState ? userIdEnabledViaSetPassword
245 : userIdDisabledViaSetPassword;
246 ipmiUserGetPrivilegeAccess(req->userId, chNum, resp->privAccess);
247 *dataLen = sizeof(*resp);
248
249 return IPMI_CC_OK;
250}
251
252ipmi_ret_t ipmiSetUserName(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
253 ipmi_request_t request, ipmi_response_t response,
254 ipmi_data_len_t dataLen, ipmi_context_t context)
255{
256 const SetUserNameReq* req = static_cast<SetUserNameReq*>(request);
257 size_t reqLength = *dataLen;
258 *dataLen = 0;
259
260 if (reqLength != sizeof(*req))
261 {
262 log<level::DEBUG>("Set user name - Invalid Length");
263 return IPMI_CC_REQ_DATA_LEN_INVALID;
264 }
265 if (req->reserved1)
266 {
267 return IPMI_CC_INVALID_FIELD_REQUEST;
268 }
269 if (!ipmiUserIsValidUserId(req->userId))
270 {
271 log<level::DEBUG>("Set user name - Invalid user id");
272 return IPMI_CC_PARM_OUT_OF_RANGE;
273 }
274
275 return ipmiUserSetUserName(req->userId,
276 reinterpret_cast<const char*>(req->userName));
277}
278
279/** @brief implementes the get user name command
280 * @param[in] netfn - specifies netfn.
281 * @param[in] cmd - specifies cmd number.
282 * @param[in] request - pointer to request data.
283 * @param[in, out] dataLen - specifies request data length, and returns
284 * response data length.
285 * @param[in] context - ipmi context.
286 * @returns ipmi completion code.
287 */
288ipmi_ret_t ipmiGetUserName(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
289 ipmi_request_t request, ipmi_response_t response,
290 ipmi_data_len_t dataLen, ipmi_context_t context)
291{
292 const GetUserNameReq* req = static_cast<GetUserNameReq*>(request);
293 size_t reqLength = *dataLen;
294
295 *dataLen = 0;
296
297 if (reqLength != sizeof(*req))
298 {
299 log<level::DEBUG>("Get user name - Invalid Length");
300 return IPMI_CC_REQ_DATA_LEN_INVALID;
301 }
302
303 std::string userName;
304 if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK)
305 { // Invalid User ID
306 log<level::DEBUG>("User Name not found",
307 entry("USER-ID:%d", (uint8_t)req->userId));
308 return IPMI_CC_PARM_OUT_OF_RANGE;
309 }
310 GetUserNameResp* resp = static_cast<GetUserNameResp*>(response);
311 std::fill(reinterpret_cast<uint8_t*>(resp),
312 reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
313 userName.copy(reinterpret_cast<char*>(resp->userName),
314 sizeof(resp->userName), 0);
315 *dataLen = sizeof(*resp);
316
317 return IPMI_CC_OK;
318}
319
320int pamFunctionConversation(int numMsg, const struct pam_message** msg,
321 struct pam_response** resp, void* appdataPtr)
322{
323 if (appdataPtr == nullptr)
324 {
325 return PAM_AUTH_ERR;
326 }
327 size_t passSize = std::strlen(reinterpret_cast<char*>(appdataPtr)) + 1;
328 char* pass = reinterpret_cast<char*>(malloc(passSize));
329 std::strncpy(pass, reinterpret_cast<char*>(appdataPtr), passSize);
330
331 *resp = reinterpret_cast<pam_response*>(
332 calloc(numMsg, sizeof(struct pam_response)));
333
334 for (int i = 0; i < numMsg; ++i)
335 {
336 if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
337 {
338 continue;
339 }
340 resp[i]->resp = pass;
341 }
342 return PAM_SUCCESS;
343}
344
345bool pamUpdatePasswd(const char* username, const char* password)
346{
347 const struct pam_conv localConversation = {pamFunctionConversation,
348 const_cast<char*>(password)};
349 pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
350
351 if (pam_start("passwd", username, &localConversation, &localAuthHandle) !=
352 PAM_SUCCESS)
353 {
354 return false;
355 }
356 int retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
357
358 if (retval != PAM_SUCCESS)
359 {
360 if (retval == PAM_AUTHTOK_ERR)
361 {
362 log<level::DEBUG>("Authentication Failure");
363 }
364 else
365 {
366 log<level::DEBUG>("pam_chauthtok returned failure",
367 entry("ERROR=%d", retval));
368 }
369 pam_end(localAuthHandle, retval);
370 return false;
371 }
372 if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
373 {
374 return false;
375 }
376 return true;
377}
378
379/** @brief implementes the set user password command
380 * @param[in] netfn - specifies netfn.
381 * @param[in] cmd - specifies cmd number.
382 * @param[in] request - pointer to request data.
383 * @param[in, out] dataLen - specifies request data length, and returns
384 * response data length.
385 * @param[in] context - ipmi context.
386 * @returns ipmi completion code.
387 */
388ipmi_ret_t ipmiSetUserPassword(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
389 ipmi_request_t request, ipmi_response_t response,
390 ipmi_data_len_t dataLen, ipmi_context_t context)
391{
392 const SetUserPasswordReq* req = static_cast<SetUserPasswordReq*>(request);
393 size_t reqLength = *dataLen;
394 // subtract 2 bytes header to know the password length - including NULL
395 uint8_t passwordLength = *dataLen - 2;
396 *dataLen = 0;
397
398 // verify input length based on operation. Required password size is 20
399 // bytes as we support only IPMI 2.0, but in order to be compatible with
400 // tools, accept 16 bytes of password size too.
401 if (reqLength < 2 ||
402 // If enable / disable user, reqLength has to be >=2 & <= 22
403 ((req->operation == disableUser || req->operation == enableUser) &&
404 ((reqLength < 2) || (reqLength > sizeof(SetUserPasswordReq)))))
405 {
406 log<level::DEBUG>("Invalid Length");
407 return IPMI_CC_REQ_DATA_LEN_INVALID;
408 }
409 // If set / test password then password length has to be 16 or 20 bytes
410 if (((req->operation == setPassword) || (req->operation == testPassword)) &&
411 ((passwordLength != maxIpmi20PasswordSize) &&
412 (passwordLength != maxIpmi15PasswordSize)))
413 {
414 log<level::DEBUG>("Invalid Length");
415 return IPMI_CC_REQ_DATA_LEN_INVALID;
416 }
417
418 std::string userName;
419 if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK)
420 {
421 log<level::DEBUG>("User Name not found",
422 entry("USER-ID:%d", (uint8_t)req->userId));
423 return IPMI_CC_PARM_OUT_OF_RANGE;
424 }
425 if (req->operation == setPassword)
426 {
427 std::string passwd;
428 passwd.assign(reinterpret_cast<const char*>(req->userPassword), 0,
429 maxIpmi20PasswordSize);
430 if (!std::regex_match(passwd.c_str(),
431 std::regex("[a-zA-z_0-9][a-zA-Z_0-9,?:`!\"]*")))
432 {
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530433 log<level::ERR>("Invalid password fields",
434 entry("USER-ID:%d", (uint8_t)req->userId));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530435 return IPMI_CC_INVALID_FIELD_REQUEST;
436 }
437 if (!pamUpdatePasswd(userName.c_str(), passwd.c_str()))
438 {
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530439 log<level::ERR>("Failed to update password",
440 entry("USER-ID:%d", (uint8_t)req->userId));
441 return IPMI_CC_INVALID_FIELD_REQUEST;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530442 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530443 return IPMI_CC_OK;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530444 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530445 else if (req->operation == enableUser || req->operation == disableUser)
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530446 {
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530447 return ipmiUserUpdateEnabledState(req->userId,
448 static_cast<bool>(req->operation));
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530449 }
Richard Marian Thomaiyar282e79b2018-11-13 19:00:58 +0530450 else if (req->operation == testPassword)
451 {
452 auto password = ipmiUserGetPassword(userName);
453 std::string testPassword(
454 reinterpret_cast<const char*>(req->userPassword), 0,
455 passwordLength);
456 // Note: For security reasons password size won't be compared and
457 // wrong password size completion code will not be returned if size
458 // doesn't match as specified in IPMI specification.
459 if (password != testPassword)
460 {
461 log<level::DEBUG>("Test password failed",
462 entry("USER-ID:%d", (uint8_t)req->userId));
463 return static_cast<ipmi_ret_t>(
464 IPMISetPasswordReturnCodes::ipmiCCPasswdFailMismatch);
465 }
466 return IPMI_CC_OK;
467 }
468 return IPMI_CC_INVALID_FIELD_REQUEST;
Richard Marian Thomaiyar5a6b6362018-03-12 23:42:34 +0530469}
470
471void registerUserIpmiFunctions()
472{
473 ipmiUserInit();
474 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_ACCESS, NULL,
475 ipmiSetUserAccess, PRIVILEGE_ADMIN);
476
477 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_USER_ACCESS, NULL,
478 ipmiGetUserAccess, PRIVILEGE_OPERATOR);
479
480 ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_USER_NAME, NULL,
481 ipmiGetUserName, PRIVILEGE_OPERATOR);
482
483 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_NAME, NULL,
484 ipmiSetUserName, PRIVILEGE_ADMIN);
485
486 ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_PASSWORD, NULL,
487 ipmiSetUserPassword, PRIVILEGE_ADMIN);
488
489 return;
490}
491} // namespace ipmi