| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "apr_private.h" |
| #include "apr_strings.h" |
| #include "apr_portable.h" |
| #include "apr_user.h" |
| #include "apr_arch_file_io.h" |
| #if APR_HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| |
| /* Internal sid binary to string translation, see MSKB Q131320. |
| * Several user related operations require our SID to access |
| * the registry, but in a string format. All error handling |
| * depends on IsValidSid(), which internally we better test long |
| * before we get here! |
| */ |
| static void get_sid_string(char *buf, apr_size_t blen, apr_uid_t id) |
| { |
| PSID_IDENTIFIER_AUTHORITY psia; |
| DWORD nsa; |
| DWORD sa; |
| int slen; |
| |
| /* Determine authority values (these is a big-endian value, |
| * and NT records the value as hex if the value is > 2^32.) |
| */ |
| psia = GetSidIdentifierAuthority(id); |
| nsa = (DWORD)(psia->Value[5]) + ((DWORD)(psia->Value[4]) << 8) |
| + ((DWORD)(psia->Value[3]) << 16) + ((DWORD)(psia->Value[2]) << 24); |
| sa = (DWORD)(psia->Value[1]) + ((DWORD)(psia->Value[0]) << 8); |
| if (sa) { |
| slen = apr_snprintf(buf, blen, "S-%d-0x%04x%08x", |
| SID_REVISION, (unsigned int)sa, (unsigned int)nsa); |
| } else { |
| slen = apr_snprintf(buf, blen, "S-%d-%lu", |
| SID_REVISION, nsa); |
| } |
| |
| /* Now append all the subauthority strings. |
| */ |
| nsa = *GetSidSubAuthorityCount(id); |
| for (sa = 0; sa < nsa; ++sa) { |
| slen += apr_snprintf(buf + slen, blen - slen, "-%lu", |
| *GetSidSubAuthority(id, sa)); |
| } |
| } |
| |
| /* Query the ProfileImagePath from the version-specific branch, where the |
| * regkey uses the user's name on 9x, and user's sid string on NT. |
| */ |
| APR_DECLARE(apr_status_t) apr_uid_homepath_get(char **dirname, |
| const char *username, |
| apr_pool_t *p) |
| { |
| apr_status_t rv; |
| char regkey[MAX_PATH * 2]; |
| char *fixch; |
| DWORD keylen; |
| DWORD type; |
| HKEY key; |
| |
| if (apr_os_level >= APR_WIN_NT) { |
| apr_uid_t uid; |
| apr_gid_t gid; |
| |
| if ((rv = apr_uid_get(&uid, &gid, username, p)) != APR_SUCCESS) |
| return rv; |
| |
| strcpy(regkey, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\" |
| "ProfileList\\"); |
| keylen = (DWORD)strlen(regkey); |
| get_sid_string(regkey + keylen, sizeof(regkey) - keylen, uid); |
| } |
| else { |
| strcpy(regkey, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\" |
| "ProfileList\\"); |
| keylen = (DWORD)strlen(regkey); |
| apr_cpystrn(regkey + keylen, username, sizeof(regkey) - keylen); |
| } |
| |
| if ((rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey, 0, |
| KEY_QUERY_VALUE, &key)) != ERROR_SUCCESS) |
| return APR_FROM_OS_ERROR(rv); |
| |
| keylen = sizeof(regkey); |
| rv = RegQueryValueExW(key, L"ProfileImagePath", NULL, &type, |
| (void*)regkey, &keylen); |
| RegCloseKey(key); |
| if (rv != ERROR_SUCCESS) |
| return APR_FROM_OS_ERROR(rv); |
| if (type == REG_SZ) { |
| char retdir[MAX_PATH]; |
| if ((rv = unicode_to_utf8_path(retdir, sizeof(retdir), |
| (apr_wchar_t*)regkey)) != APR_SUCCESS) |
| return rv; |
| *dirname = apr_pstrdup(p, retdir); |
| } |
| else if (type == REG_EXPAND_SZ) { |
| apr_wchar_t path[MAX_PATH]; |
| char retdir[MAX_PATH]; |
| ExpandEnvironmentStringsW((apr_wchar_t*)regkey, path, |
| sizeof(path) / 2); |
| if ((rv = unicode_to_utf8_path(retdir, sizeof(retdir), path)) |
| != APR_SUCCESS) |
| return rv; |
| *dirname = apr_pstrdup(p, retdir); |
| } |
| else |
| return APR_ENOENT; |
| for (fixch = *dirname; *fixch; ++fixch) |
| if (*fixch == '\\') |
| *fixch = '/'; |
| return APR_SUCCESS; |
| } |
| |
| APR_DECLARE(apr_status_t) apr_uid_current(apr_uid_t *uid, |
| apr_gid_t *gid, |
| apr_pool_t *p) |
| { |
| HANDLE threadtok; |
| DWORD needed; |
| TOKEN_USER *usr; |
| TOKEN_PRIMARY_GROUP *grp; |
| apr_status_t rv; |
| |
| if(!OpenProcessToken(GetCurrentProcess(), STANDARD_RIGHTS_READ | READ_CONTROL | TOKEN_QUERY, &threadtok)) { |
| return apr_get_os_error(); |
| } |
| |
| *uid = NULL; |
| if (!GetTokenInformation(threadtok, TokenUser, NULL, 0, &needed) |
| && (GetLastError() == ERROR_INSUFFICIENT_BUFFER) |
| && (usr = apr_palloc(p, needed)) |
| && GetTokenInformation(threadtok, TokenUser, usr, needed, &needed)) { |
| *uid = usr->User.Sid; |
| } |
| else { |
| rv = apr_get_os_error(); |
| CloseHandle(threadtok); |
| return rv; |
| } |
| |
| if (!GetTokenInformation(threadtok, TokenPrimaryGroup, NULL, 0, &needed) |
| && (GetLastError() == ERROR_INSUFFICIENT_BUFFER) |
| && (grp = apr_palloc(p, needed)) |
| && GetTokenInformation(threadtok, TokenPrimaryGroup, grp, needed, &needed)) { |
| *gid = grp->PrimaryGroup; |
| } |
| else { |
| rv = apr_get_os_error(); |
| CloseHandle(threadtok); |
| return rv; |
| } |
| |
| CloseHandle(threadtok); |
| |
| return APR_SUCCESS; |
| } |
| |
| APR_DECLARE(apr_status_t) apr_uid_get(apr_uid_t *uid, apr_gid_t *gid, |
| const char *username, apr_pool_t *p) |
| { |
| SID_NAME_USE sidtype; |
| char anydomain[256]; |
| char *domain; |
| DWORD sidlen = 0; |
| DWORD domlen = sizeof(anydomain); |
| DWORD rv; |
| char *pos; |
| |
| if ((pos = strchr(username, '/'))) { |
| domain = apr_pstrmemdup(p, username, pos - username); |
| username = pos + 1; |
| } |
| else if ((pos = strchr(username, '\\'))) { |
| domain = apr_pstrmemdup(p, username, pos - username); |
| username = pos + 1; |
| } |
| else { |
| domain = NULL; |
| } |
| /* Get nothing on the first pass ... need to size the sid buffer |
| */ |
| rv = LookupAccountName(domain, username, domain, &sidlen, |
| anydomain, &domlen, &sidtype); |
| if (sidlen) { |
| /* Give it back on the second pass |
| */ |
| *uid = apr_palloc(p, sidlen); |
| domlen = sizeof(anydomain); |
| rv = LookupAccountName(domain, username, *uid, &sidlen, |
| anydomain, &domlen, &sidtype); |
| } |
| if (!sidlen || !rv) { |
| return apr_get_os_error(); |
| } |
| /* There doesn't seem to be a simple way to retrieve the primary group sid |
| */ |
| *gid = NULL; |
| return APR_SUCCESS; |
| } |
| |
| APR_DECLARE(apr_status_t) apr_uid_name_get(char **username, apr_uid_t userid, |
| apr_pool_t *p) |
| { |
| SID_NAME_USE type; |
| char name[MAX_PATH], domain[MAX_PATH]; |
| DWORD cbname = sizeof(name), cbdomain = sizeof(domain); |
| if (!userid) |
| return APR_EINVAL; |
| if (!LookupAccountSid(NULL, userid, name, &cbname, domain, &cbdomain, &type)) |
| return apr_get_os_error(); |
| if (type != SidTypeUser && type != SidTypeAlias && type != SidTypeWellKnownGroup) |
| return APR_EINVAL; |
| *username = apr_pstrdup(p, name); |
| return APR_SUCCESS; |
| } |
| |
| APR_DECLARE(apr_status_t) apr_uid_compare(apr_uid_t left, apr_uid_t right) |
| { |
| if (!left || !right) |
| return APR_EINVAL; |
| if (!IsValidSid(left) || !IsValidSid(right)) |
| return APR_EINVAL; |
| if (!EqualSid(left, right)) |
| return APR_EMISMATCH; |
| return APR_SUCCESS; |
| } |
| |