1 /*
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 
28 namespace ipmi
29 {
30 
31 using namespace phosphor::logging;
32 
33 static constexpr uint8_t maxIpmi20PasswordSize = 20;
34 static constexpr uint8_t maxIpmi15PasswordSize = 16;
35 static constexpr uint8_t disableUser = 0x00;
36 static constexpr uint8_t enableUser = 0x01;
37 static constexpr uint8_t setPassword = 0x02;
38 static constexpr uint8_t testPassword = 0x03;
39 
40 struct 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 
71 struct 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 
87 struct 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 
108 struct 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 
121 struct 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 
133 struct GetUserNameResp
134 {
135     uint8_t userName[16];
136 } __attribute__((packed));
137 
138 struct 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 
157 ipmi_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;
163 
164     if (!(reqLength == sizeof(*req) ||
165           (reqLength == (sizeof(*req) - sizeof(uint8_t) /* skip optional*/))))
166     {
167         log<level::DEBUG>("Set user access - Invalid Length");
168         return IPMI_CC_REQ_DATA_LEN_INVALID;
169     }
170     if (req->reserved1 != 0 || req->reserved2 != 0 || req->reserved3 != 0 ||
171         req->sessLimit != 0 ||
172         (!ipmiUserIsValidChannel(req->chNum) ||
173          (!ipmiUserIsValidPrivilege(req->privilege))))
174     // TODO: Need to check for session support and return invalid field in
175     // request
176     {
177         log<level::DEBUG>("Set user access - Invalid field in request");
178         return IPMI_CC_INVALID_FIELD_REQUEST;
179     }
180     if (!ipmiUserIsValidUserId(req->userId))
181     {
182         log<level::DEBUG>("Set user access - Parameter out of range");
183         return IPMI_CC_PARM_OUT_OF_RANGE;
184     }
185     // TODO: Determine the Channel number 0xE (Self Channel number ?)
186     uint8_t chNum = req->chNum;
187     PrivAccess privAccess = {0};
188     if (req->bitsUpdate)
189     {
190         privAccess.ipmiEnabled = req->ipmiEnabled;
191         privAccess.linkAuthEnabled = req->linkAuthEnabled;
192         privAccess.accessCallback = req->accessCallback;
193     }
194     privAccess.privilege = req->privilege;
195     ipmiUserSetPrivilegeAccess(req->userId, chNum, privAccess, req->bitsUpdate);
196 
197     return IPMI_CC_OK;
198 }
199 
200 ipmi_ret_t ipmiGetUserAccess(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
201                              ipmi_request_t request, ipmi_response_t response,
202                              ipmi_data_len_t dataLen, ipmi_context_t context)
203 {
204     const GetUserAccessReq* req = static_cast<GetUserAccessReq*>(request);
205     size_t reqLength = *dataLen;
206 
207     *dataLen = 0;
208 
209     if (reqLength != sizeof(*req))
210     {
211         log<level::DEBUG>("Get user access - Invalid Length");
212         return IPMI_CC_REQ_DATA_LEN_INVALID;
213     }
214     if (req->reserved1 != 0 || req->reserved2 != 0 ||
215         (!ipmiUserIsValidChannel(req->chNum)))
216     // TODO: Need to check for session support and return invalid field in
217     // request
218     {
219         log<level::DEBUG>("Get user access - Invalid field in request");
220         return IPMI_CC_INVALID_FIELD_REQUEST;
221     }
222     if (!ipmiUserIsValidUserId(req->userId))
223     {
224         log<level::DEBUG>("Get user access - Parameter out of range");
225         return IPMI_CC_PARM_OUT_OF_RANGE;
226     }
227 
228     uint8_t maxChUsers = 0, enabledUsers = 0, fixedUsers = 0;
229     bool enabledState = false;
230     // TODO: Determine the Channel number 0xE (Self Channel number ?)
231     uint8_t chNum = req->chNum;
232     GetUserAccessResp* resp = static_cast<GetUserAccessResp*>(response);
233 
234     std::fill(reinterpret_cast<uint8_t*>(resp),
235               reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
236 
237     ipmiUserGetAllCounts(maxChUsers, enabledUsers, fixedUsers);
238     resp->maxChUsers = maxChUsers;
239     resp->enabledUsers = enabledUsers;
240     resp->fixedUsers = fixedUsers;
241 
242     ipmiUserCheckEnabled(req->userId, enabledState);
243     resp->enabledStatus = enabledState ? userIdEnabledViaSetPassword
244                                        : userIdDisabledViaSetPassword;
245     ipmiUserGetPrivilegeAccess(req->userId, chNum, resp->privAccess);
246     *dataLen = sizeof(*resp);
247 
248     return IPMI_CC_OK;
249 }
250 
251 ipmi_ret_t ipmiSetUserName(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
252                            ipmi_request_t request, ipmi_response_t response,
253                            ipmi_data_len_t dataLen, ipmi_context_t context)
254 {
255     const SetUserNameReq* req = static_cast<SetUserNameReq*>(request);
256     size_t reqLength = *dataLen;
257     *dataLen = 0;
258 
259     if (reqLength != sizeof(*req))
260     {
261         log<level::DEBUG>("Set user name - Invalid Length");
262         return IPMI_CC_REQ_DATA_LEN_INVALID;
263     }
264     if (req->reserved1)
265     {
266         return IPMI_CC_INVALID_FIELD_REQUEST;
267     }
268     if (!ipmiUserIsValidUserId(req->userId))
269     {
270         log<level::DEBUG>("Set user name - Invalid user id");
271         return IPMI_CC_PARM_OUT_OF_RANGE;
272     }
273 
274     return ipmiUserSetUserName(req->userId,
275                                reinterpret_cast<const char*>(req->userName));
276 }
277 
278 /** @brief implementes the get user name command
279  *  @param[in] netfn - specifies netfn.
280  *  @param[in] cmd   - specifies cmd number.
281  *  @param[in] request - pointer to request data.
282  *  @param[in, out] dataLen - specifies request data length, and returns
283  * response data length.
284  *  @param[in] context - ipmi context.
285  *  @returns ipmi completion code.
286  */
287 ipmi_ret_t ipmiGetUserName(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 GetUserNameReq* req = static_cast<GetUserNameReq*>(request);
292     size_t reqLength = *dataLen;
293 
294     *dataLen = 0;
295 
296     if (reqLength != sizeof(*req))
297     {
298         log<level::DEBUG>("Get user name - Invalid Length");
299         return IPMI_CC_REQ_DATA_LEN_INVALID;
300     }
301 
302     std::string userName;
303     if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK)
304     { // Invalid User ID
305         log<level::DEBUG>("User Name not found",
306                           entry("USER-ID:%d", (uint8_t)req->userId));
307         return IPMI_CC_PARM_OUT_OF_RANGE;
308     }
309     GetUserNameResp* resp = static_cast<GetUserNameResp*>(response);
310     std::fill(reinterpret_cast<uint8_t*>(resp),
311               reinterpret_cast<uint8_t*>(resp) + sizeof(*resp), 0);
312     userName.copy(reinterpret_cast<char*>(resp->userName),
313                   sizeof(resp->userName), 0);
314     *dataLen = sizeof(*resp);
315 
316     return IPMI_CC_OK;
317 }
318 
319 int pamFunctionConversation(int numMsg, const struct pam_message** msg,
320                             struct pam_response** resp, void* appdataPtr)
321 {
322     if (appdataPtr == nullptr)
323     {
324         return PAM_AUTH_ERR;
325     }
326     size_t passSize = std::strlen(reinterpret_cast<char*>(appdataPtr)) + 1;
327     char* pass = reinterpret_cast<char*>(malloc(passSize));
328     std::strncpy(pass, reinterpret_cast<char*>(appdataPtr), passSize);
329 
330     *resp = reinterpret_cast<pam_response*>(
331         calloc(numMsg, sizeof(struct pam_response)));
332 
333     for (int i = 0; i < numMsg; ++i)
334     {
335         if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
336         {
337             continue;
338         }
339         resp[i]->resp = pass;
340     }
341     return PAM_SUCCESS;
342 }
343 
344 bool pamUpdatePasswd(const char* username, const char* password)
345 {
346     const struct pam_conv localConversation = {pamFunctionConversation,
347                                                const_cast<char*>(password)};
348     pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
349 
350     if (pam_start("passwd", username, &localConversation, &localAuthHandle) !=
351         PAM_SUCCESS)
352     {
353         return false;
354     }
355     int retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
356 
357     if (retval != PAM_SUCCESS)
358     {
359         if (retval == PAM_AUTHTOK_ERR)
360         {
361             log<level::DEBUG>("Authentication Failure");
362         }
363         else
364         {
365             log<level::DEBUG>("pam_chauthtok returned failure",
366                               entry("ERROR=%d", retval));
367         }
368         pam_end(localAuthHandle, retval);
369         return false;
370     }
371     if (pam_end(localAuthHandle, PAM_SUCCESS) != PAM_SUCCESS)
372     {
373         return false;
374     }
375     return true;
376 }
377 
378 /** @brief implementes the set user password command
379  *  @param[in] netfn - specifies netfn.
380  *  @param[in] cmd   - specifies cmd number.
381  *  @param[in] request - pointer to request data.
382  *  @param[in, out] dataLen - specifies request data length, and returns
383  * response data length.
384  *  @param[in] context - ipmi context.
385  *  @returns ipmi completion code.
386  */
387 ipmi_ret_t ipmiSetUserPassword(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
388                                ipmi_request_t request, ipmi_response_t response,
389                                ipmi_data_len_t dataLen, ipmi_context_t context)
390 {
391     const SetUserPasswordReq* req = static_cast<SetUserPasswordReq*>(request);
392     size_t reqLength = *dataLen;
393     // subtract 2 bytes header to know the password length - including NULL
394     uint8_t passwordLength = *dataLen - 2;
395     *dataLen = 0;
396 
397     // verify input length based on operation. Required password size is 20
398     // bytes as  we support only IPMI 2.0, but in order to be compatible with
399     // tools, accept 16 bytes of password size too.
400     if (reqLength < 2 ||
401         // If enable / disable user, reqLength has to be >=2 & <= 22
402         ((req->operation == disableUser || req->operation == enableUser) &&
403          ((reqLength < 2) || (reqLength > sizeof(SetUserPasswordReq)))))
404     {
405         log<level::DEBUG>("Invalid Length");
406         return IPMI_CC_REQ_DATA_LEN_INVALID;
407     }
408     // If set / test password then password length has to be 16 or 20 bytes
409     if (((req->operation == setPassword) || (req->operation == testPassword)) &&
410         ((passwordLength != maxIpmi20PasswordSize) &&
411          (passwordLength != maxIpmi15PasswordSize)))
412     {
413         log<level::DEBUG>("Invalid Length");
414         return IPMI_CC_REQ_DATA_LEN_INVALID;
415     }
416 
417     std::string userName;
418     if (ipmiUserGetUserName(req->userId, userName) != IPMI_CC_OK)
419     {
420         log<level::DEBUG>("User Name not found",
421                           entry("USER-ID:%d", (uint8_t)req->userId));
422         return IPMI_CC_PARM_OUT_OF_RANGE;
423     }
424     if (req->operation == setPassword)
425     {
426         std::string passwd;
427         passwd.assign(reinterpret_cast<const char*>(req->userPassword), 0,
428                       maxIpmi20PasswordSize);
429         if (!std::regex_match(passwd.c_str(),
430                               std::regex("[a-zA-z_0-9][a-zA-Z_0-9,?:`!\"]*")))
431         {
432             log<level::ERR>("Invalid password fields",
433                             entry("USER-ID:%d", (uint8_t)req->userId));
434             return IPMI_CC_INVALID_FIELD_REQUEST;
435         }
436         if (!pamUpdatePasswd(userName.c_str(), passwd.c_str()))
437         {
438             log<level::ERR>("Failed to update password",
439                             entry("USER-ID:%d", (uint8_t)req->userId));
440             return IPMI_CC_INVALID_FIELD_REQUEST;
441         }
442         return IPMI_CC_OK;
443     }
444     else if (req->operation == enableUser || req->operation == disableUser)
445     {
446         return ipmiUserUpdateEnabledState(req->userId,
447                                           static_cast<bool>(req->operation));
448     }
449     else if (req->operation == testPassword)
450     {
451         auto password = ipmiUserGetPassword(userName);
452         std::string testPassword(
453             reinterpret_cast<const char*>(req->userPassword), 0,
454             passwordLength);
455         // Note: For security reasons password size won't be compared and
456         // wrong password size completion code will not be returned if size
457         // doesn't match as specified in IPMI specification.
458         if (password != testPassword)
459         {
460             log<level::DEBUG>("Test password failed",
461                               entry("USER-ID:%d", (uint8_t)req->userId));
462             return static_cast<ipmi_ret_t>(
463                 IPMISetPasswordReturnCodes::ipmiCCPasswdFailMismatch);
464         }
465         return IPMI_CC_OK;
466     }
467     return IPMI_CC_INVALID_FIELD_REQUEST;
468 }
469 
470 void registerUserIpmiFunctions()
471 {
472     ipmiUserInit();
473     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_ACCESS, NULL,
474                            ipmiSetUserAccess, PRIVILEGE_ADMIN);
475 
476     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_USER_ACCESS, NULL,
477                            ipmiGetUserAccess, PRIVILEGE_OPERATOR);
478 
479     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_USER_NAME, NULL,
480                            ipmiGetUserName, PRIVILEGE_OPERATOR);
481 
482     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_NAME, NULL,
483                            ipmiSetUserName, PRIVILEGE_ADMIN);
484 
485     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_USER_PASSWORD, NULL,
486                            ipmiSetUserPassword, PRIVILEGE_ADMIN);
487 
488     return;
489 }
490 } // namespace ipmi
491