/* **============================================================================== ** ** Open Management Infrastructure (OMI) ** ** Copyright (c) Microsoft Corporation ** ** Licensed under the Apache License, Version 2.0 (the "License"); you may not ** use this file except in compliance with the License. You may obtain a copy ** of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, ** MERCHANTABLITY OR NON-INFRINGEMENT. ** ** See the Apache 2 License for the specific language governing permissions ** and limitations under the License. ** **============================================================================== */ #include "user.h" #include "log.h" #if defined(CONFIG_POSIX) # include "credcache.h" # include # include # include # include # if defined(CONFIG_OS_DARWIN) && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ <= 1058 # include # else # include # endif # include # include # include # include # include # include # include #elif defined(CONFIG_OS_WINDOWS) #endif #include "paths.h" #define USERNAME_SIZE 128 #define GROUPNAME_SIZE 128 #if defined(CONFIG_POSIX) static int s_ignoreAuthCalls; #endif #if defined(CONFIG_OS_WINDOWS) /* Validates user name and password; Returns: '0' if user account is valid and authorized to use CIM server '-1' otherwise */ int AuthenticateUser(const char* user, const char* password) { MI_UNUSED(user); MI_UNUSED(password); return 0; } /* Validates user's account for correct account name, expiration etc. Returns: '0' if user account is valid and authorized to use CIM server '-1' otherwise */ int ValidateUser(const char* user) { MI_UNUSED(user); return 0; } int LookupUser(const char* user, uid_t* uid, gid_t* gid) { MI_UNUSED(user); *uid = -1; *gid = -1; return 0; } int GetUserGidByUid(uid_t uid, gid_t* gid) { MI_UNUSED(uid); *gid = -1; return 0; } int GetUIDByConnection(int fd, uid_t* uid, gid_t* gid) { MI_UNUSED(fd); *uid = -1; *gid = -1; return -1; } /* Creates file with random data owned by user and RO by user only Parameters: uid - user ID size - number of bytes to write path - [out] - resulting file name Returns: 0 if operation was successful; -1 otherwise */ int CreateAuthFile( uid_t uid, char* content, size_t size, char path[MAX_PATH_SIZE]) { MI_UNUSED(uid); MI_UNUSED(size); MI_UNUSED(path); MI_UNUSED(content); return -1; } #elif defined(CONFIG_POSIX) static int GetUserName( uid_t uid, char name[USERNAME_SIZE]); static int GetGroupName( gid_t gid, char name[GROUPNAME_SIZE]); static int _authCallback( int numMessages, #if defined(CONFIG_OS_LINUX) || defined(CONFIG_OS_DARWIN) const struct pam_message** messages, #else struct pam_message** messages, #endif struct pam_response** response, void* applicationData) { const char* password = (const char*)applicationData; int i; /* If zero (or megative) messages, return now */ if (numMessages <= 0) { return PAM_CONV_ERR; } /* Allocate the responses */ *response = (struct pam_response*)calloc( numMessages, sizeof(struct pam_response)); if (!*response) { return PAM_BUF_ERR; } /* Copy the password to the response messages */ for (i = 0; i < numMessages; i++) { if (PAM_PROMPT_ECHO_OFF == messages[i]->msg_style) { response[i]->resp = (char*)malloc(PAM_MAX_MSG_SIZE); Strlcpy(response[i]->resp, password, PAM_MAX_MSG_SIZE); response[i]->resp_retcode = 0; } else { return PAM_CONV_ERR; } } return PAM_SUCCESS; } static int _PamCheckUser( const char* user, const char* password) { struct pam_conv conv; pam_handle_t* t = 0; memset(&conv, 0, sizeof(conv)); conv.conv = _authCallback; conv.appdata_ptr = (void*)password; if (PAM_SUCCESS != pam_start("omi", user, &conv, &t)) return -1; if (PAM_SUCCESS != pam_authenticate(t, 0)) { pam_end(t,0); return -1; } if (PAM_SUCCESS != pam_acct_mgmt(t, 0)) { pam_end(t,0); return -1; } pam_end(t, 0); return 0; } static int _CreateChildProcess( int* fd, pid_t* child, const char* user, const char* password) { int s[2]; int fdLimit; /* create communication pipe */ if(0 != socketpair(AF_UNIX, SOCK_STREAM, 0, s)) { LOGW_CHAR(("socketpair() failed\n")); return -1; } *child = fork(); if (*child < 0) { close(s[0]); close(s[1]); return -1; /* Failed */ } if (*child > 0) { close(s[1]); *fd = s[0]; return 0; /* Started */ } /* We are in child process here */ /* Close all open file descriptors except provided socket (Some systems have UNLIMITED of 2^64; limit to something reasonable) */ fdLimit = getdtablesize(); if (fdLimit > 2500 || fdLimit < 0) { fdLimit = 2500; } /* ATTN: close first 3 also! Left for debugging only */ { int i; for (i = 3; i < fdLimit; ++i) { if (i != s[1]) close(i); } } /* perform operation in quesiton */ { int r = _PamCheckUser(user, password); write(s[1], &r, sizeof(r)); close(s[1]); } _exit(0); } /* Validates user name and password; Returns: '0' if user account is valid and authorized to use CIM server '-1' otherwise */ int AuthenticateUser(const char* user, const char* password) { int fd = 0; pid_t child = 0; int r = -1; if (s_ignoreAuthCalls) return 0; /* Verify if user is in cache already */ if (0 == CredCache_CheckUser(user, password)) return 0; if (0 != _CreateChildProcess(&fd, &child, user, password)) { return -1; } if (sizeof(int) != read(fd, &r, sizeof(int))) { LOGE_CHAR(("failed to read result from child")); r = -1; goto done; } done: close(fd); /* SIGCHILD HANDLER will take care of pid waiting */ #if 0 { int loc = 0; pid_t p = waitpid(child, &loc, 0 /*WNOHANG*/); if (p != child) LOGW_CHAR(("waitpid returned %d, loc %d",(int)p, loc)); } #endif /* Add user to cache if auth was ok */ if (0 == r) CredCache_PutUser(user, password); return r; } /* Validates user's account for correct account name, expiration etc. Returns: '0' if user account is valid and authorized to use CIM server '-1' otherwise */ int ValidateUser(const char* user) { if (s_ignoreAuthCalls) return 0; MI_UNUSED(user); return 0; } /* Disables authentication calls so 'AuthUser' always retunrs 'ok'; used for unit-test only */ void IgnoreAuthCalls(int flag) { s_ignoreAuthCalls = flag; } /* Looks for user's account and retrieves uid/gid. Parameters: user - user name uid [out] user ID gid [out] group ID Returns: 0 if operation was successful; -1 otherwise */ int LookupUser(const char* user, uid_t* uid, gid_t* gid) { char buf[1024]; struct passwd pwd; struct passwd* ppwd = 0; if (s_ignoreAuthCalls) return 0; if (0 != getpwnam_r(user, &pwd, buf, sizeof(buf), &ppwd) || !ppwd) { LOGW_CHAR(("getpwnam_r filed, errno %d", errno)); return -1; } *uid = pwd.pw_uid; *gid = pwd.pw_gid; return 0; } int GetUserGidByUid(uid_t uid, gid_t* gid) { /* user name */ char name[USERNAME_SIZE]; if (0 != GetUserName(uid, name)) return -1; return LookupUser(name, &uid, gid); } /* Changes user/group IDs of current process. Parameters: uid - user ID gid - group ID Returns: 0 if operation was successful; -1 otherwise */ int SetUser(uid_t uid, gid_t gid) { if (s_ignoreAuthCalls) return 0; if (setgid(gid) != 0) { LOGW_CHAR(("setgid failed: errno (%d)",errno)); return -1; } if (setuid(uid) != 0) { LOGW_CHAR(("setuid failed: errno (%d)",errno)); return -1; } return 0; } /* Verifies if current process is running as root Returns: 0 - current process is root -1 - current process is not root. */ int IsRoot() { uid_t uid = geteuid(); return uid == 0 ? 0 : -1; } int GetUIDByConnection(int fd, uid_t* uid, gid_t* gid) { #if defined(CONFIG_OS_LINUX) struct ucred credentials; socklen_t ucred_size = (socklen_t)sizeof(credentials); /*fill in the user data structure */ if(getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_size)) return -1; /* the process ID of the process on the other side of the socket */ // credentials.pid; /* the effective UID of the process on the other side of the socket */ *uid = credentials.uid; /* the effective primary GID of the process on the other side of the socket */ *gid = credentials.gid; return 0; #else MI_UNUSED(fd); *uid = -1; *gid = -1; return -1; #endif } /* Creates file with random data owned by user and RO by user only Parameters: uid - user ID size - number of bytes to write path - [out] - resulting file name Returns: 0 if operation was successful; -1 otherwise */ int CreateAuthFile(uid_t uid, char* content, size_t size, char path[MAX_PATH_SIZE]) { static MI_Uint32 counter; counter ++; /* Format file name as /. */ { /* user name */ char name[USERNAME_SIZE]; char numBuf[12]; size_t dummy; if (0 != GetUserName(uid, name)) return -1; if (Strlcpy(path, GetPath(ID_AUTHDIR), MAX_PATH_SIZE) >= MAX_PATH_SIZE) return -1; if (Strlcat(path, "/", MAX_PATH_SIZE) >= MAX_PATH_SIZE) return -1; if (Strlcat(path, name, MAX_PATH_SIZE) >= MAX_PATH_SIZE) return -1; if (Strlcat(path, ".", MAX_PATH_SIZE) >= MAX_PATH_SIZE) return -1; if (Strlcat(path, Uint32ToStr(numBuf, counter, &dummy), MAX_PATH_SIZE) >= MAX_PATH_SIZE) return -1; } /* Generate random */ if (0 != CredCache_GenerateRandom(content, size)) return -1; /* write file */ unlink(path); { int fd; /* Create auth file */ fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR); if (fd == -1) { LOGW_CHAR(("failed to create auth file %s", path)); return -1; } if (write(fd, content, size) != (int)size) { close(fd); unlink(path); return -1; } if (0 != fchown(fd, uid, -1)) { LOGW_CHAR(("failed to chown auth file %s", path)); close(fd); unlink(path); return -1; } close(fd); } return 0; } static int GetUserName( uid_t uid, char name[USERNAME_SIZE]) { struct passwd pwbuf; char buf[1024]; struct passwd* pw; if (getpwuid_r(uid, &pwbuf, buf, sizeof(buf), &pw) != 0) return -1; if (!pw) return -1; if (Strlcpy(name, pw->pw_name, USERNAME_SIZE) >= USERNAME_SIZE) return -1; return 0; } static int GetGroupName( gid_t gid, char name[GROUPNAME_SIZE]) { struct group grbuf; char buf[1024]; struct group* gr; if (getgrgid_r(gid, &grbuf, buf, sizeof(buf), &gr) != 0) return -1; if (!gr) return -1; if (Strlcpy(name, gr->gr_name, GROUPNAME_SIZE) >= GROUPNAME_SIZE) return -1; return 0; } int FormatLogFileName( uid_t uid, gid_t gid, char path[MAX_PATH_SIZE]) { char user[USERNAME_SIZE]; char group[GROUPNAME_SIZE]; if (Strlcpy(path, GetPath(ID_LOGDIR), MAX_PATH_SIZE) >= MAX_PATH_SIZE) return -1; Strlcat(path, "/omiagent.", MAX_PATH_SIZE); /* Append user name */ if (GetUserName(getuid(), user) == 0) { Strlcat(path, user, MAX_PATH_SIZE); } else { char buf[11]; sprintf(buf, "%u", getuid()); Strlcat(path, buf, MAX_PATH_SIZE); } /* Append group name */ if (GetGroupName(getgid(), group) == 0) { Strlcat(path, ".", MAX_PATH_SIZE); Strlcat(path, group, MAX_PATH_SIZE); } else { char buf[11]; sprintf(buf, "%u", getgid()); Strlcat(path, ".", MAX_PATH_SIZE); Strlcat(path, buf, MAX_PATH_SIZE); } Strlcat(path, ".log", MAX_PATH_SIZE); return 0; } #endif