1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  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 "apr_private.h"
18#include "apr_strings.h"
19#include "apr_portable.h"
20#include "apr_user.h"
21#include "apr_arch_file_io.h"
22#if APR_HAVE_SYS_TYPES_H
23#include <sys/types.h>
24#endif
25
26#ifndef _WIN32_WCE
27/* Internal sid binary to string translation, see MSKB Q131320.
28 * Several user related operations require our SID to access
29 * the registry, but in a string format.  All error handling
30 * depends on IsValidSid(), which internally we better test long
31 * before we get here!
32 */
33static void get_sid_string(char *buf, apr_size_t blen, apr_uid_t id)
34{
35    PSID_IDENTIFIER_AUTHORITY psia;
36    DWORD nsa;
37    DWORD sa;
38    int slen;
39
40    /* Determine authority values (these is a big-endian value,
41     * and NT records the value as hex if the value is > 2^32.)
42     */
43    psia = GetSidIdentifierAuthority(id);
44    nsa =  (DWORD)(psia->Value[5])        + ((DWORD)(psia->Value[4]) <<  8)
45        + ((DWORD)(psia->Value[3]) << 16) + ((DWORD)(psia->Value[2]) << 24);
46    sa  =  (DWORD)(psia->Value[1])        + ((DWORD)(psia->Value[0]) <<  8);
47    if (sa) {
48        slen = apr_snprintf(buf, blen, "S-%d-0x%04x%08x",
49                            SID_REVISION, (unsigned int)sa, (unsigned int)nsa);
50    } else {
51        slen = apr_snprintf(buf, blen, "S-%d-%lu",
52                            SID_REVISION, nsa);
53    }
54
55    /* Now append all the subauthority strings.
56     */
57    nsa = *GetSidSubAuthorityCount(id);
58    for (sa = 0; sa < nsa; ++sa) {
59        slen += apr_snprintf(buf + slen, blen - slen, "-%lu",
60                             *GetSidSubAuthority(id, sa));
61    }
62}
63#endif
64/* Query the ProfileImagePath from the version-specific branch, where the
65 * regkey uses the user's name on 9x, and user's sid string on NT.
66 */
67APR_DECLARE(apr_status_t) apr_uid_homepath_get(char **dirname,
68                                               const char *username,
69                                               apr_pool_t *p)
70{
71#ifdef _WIN32_WCE
72    *dirname = apr_pstrdup(p, "/My Documents");
73    return APR_SUCCESS;
74#else
75    apr_status_t rv;
76    char regkey[MAX_PATH * 2];
77    char *fixch;
78    DWORD keylen;
79    DWORD type;
80    HKEY key;
81
82    if (apr_os_level >= APR_WIN_NT) {
83        apr_uid_t uid;
84        apr_gid_t gid;
85
86        if ((rv = apr_uid_get(&uid, &gid, username, p)) != APR_SUCCESS)
87            return rv;
88
89        strcpy(regkey, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\"
90                       "ProfileList\\");
91        keylen = (DWORD)strlen(regkey);
92        get_sid_string(regkey + keylen, sizeof(regkey) - keylen, uid);
93    }
94    else {
95        strcpy(regkey, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\"
96                       "ProfileList\\");
97        keylen = (DWORD)strlen(regkey);
98        apr_cpystrn(regkey + keylen, username, sizeof(regkey) - keylen);
99    }
100
101    if ((rv = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regkey, 0,
102                           KEY_QUERY_VALUE, &key)) != ERROR_SUCCESS)
103        return APR_FROM_OS_ERROR(rv);
104
105#if APR_HAS_UNICODE_FS
106    IF_WIN_OS_IS_UNICODE
107    {
108        keylen = sizeof(regkey);
109        rv = RegQueryValueExW(key, L"ProfileImagePath", NULL, &type,
110                                   (void*)regkey, &keylen);
111        RegCloseKey(key);
112        if (rv != ERROR_SUCCESS)
113            return APR_FROM_OS_ERROR(rv);
114        if (type == REG_SZ) {
115            char retdir[MAX_PATH];
116            if ((rv = unicode_to_utf8_path(retdir, sizeof(retdir),
117                                           (apr_wchar_t*)regkey)) != APR_SUCCESS)
118                return rv;
119            *dirname = apr_pstrdup(p, retdir);
120        }
121        else if (type == REG_EXPAND_SZ) {
122            apr_wchar_t path[MAX_PATH];
123            char retdir[MAX_PATH];
124            ExpandEnvironmentStringsW((apr_wchar_t*)regkey, path,
125                                      sizeof(path) / 2);
126            if ((rv = unicode_to_utf8_path(retdir, sizeof(retdir), path))
127                    != APR_SUCCESS)
128                return rv;
129            *dirname = apr_pstrdup(p, retdir);
130        }
131        else
132            return APR_ENOENT;
133    }
134#endif
135#if APR_HAS_ANSI_FS
136    ELSE_WIN_OS_IS_ANSI
137    {
138        keylen = sizeof(regkey);
139        rv = RegQueryValueEx(key, "ProfileImagePath", NULL, &type,
140                                  (void*)regkey, &keylen);
141        RegCloseKey(key);
142        if (rv != ERROR_SUCCESS)
143            return APR_FROM_OS_ERROR(rv);
144        if (type == REG_SZ) {
145            *dirname = apr_pstrdup(p, regkey);
146        }
147        else if (type == REG_EXPAND_SZ) {
148            char path[MAX_PATH];
149            ExpandEnvironmentStrings(regkey, path, sizeof(path));
150            *dirname = apr_pstrdup(p, path);
151        }
152        else
153            return APR_ENOENT;
154    }
155#endif /* APR_HAS_ANSI_FS */
156    for (fixch = *dirname; *fixch; ++fixch)
157        if (*fixch == '\\')
158            *fixch = '/';
159    return APR_SUCCESS;
160#endif /* _WIN32_WCE */
161}
162
163APR_DECLARE(apr_status_t) apr_uid_current(apr_uid_t *uid,
164                                          apr_gid_t *gid,
165                                          apr_pool_t *p)
166{
167#ifdef _WIN32_WCE
168    return APR_ENOTIMPL;
169#else
170    HANDLE threadtok;
171    DWORD needed;
172    TOKEN_USER *usr;
173    TOKEN_PRIMARY_GROUP *grp;
174
175    if(!OpenProcessToken(GetCurrentProcess(), STANDARD_RIGHTS_READ | READ_CONTROL | TOKEN_QUERY, &threadtok)) {
176        return apr_get_os_error();
177    }
178
179    *uid = NULL;
180    if (!GetTokenInformation(threadtok, TokenUser, NULL, 0, &needed)
181        && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
182        && (usr = apr_palloc(p, needed))
183        && GetTokenInformation(threadtok, TokenUser, usr, needed, &needed))
184        *uid = usr->User.Sid;
185    else
186        return apr_get_os_error();
187
188    if (!GetTokenInformation(threadtok, TokenPrimaryGroup, NULL, 0, &needed)
189        && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
190        && (grp = apr_palloc(p, needed))
191        && GetTokenInformation(threadtok, TokenPrimaryGroup, grp, needed, &needed))
192        *gid = grp->PrimaryGroup;
193    else
194        return apr_get_os_error();
195
196    return APR_SUCCESS;
197#endif
198}
199
200APR_DECLARE(apr_status_t) apr_uid_get(apr_uid_t *uid, apr_gid_t *gid,
201                                      const char *username, apr_pool_t *p)
202{
203#ifdef _WIN32_WCE
204    return APR_ENOTIMPL;
205#else
206    SID_NAME_USE sidtype;
207    char anydomain[256];
208    char *domain;
209    DWORD sidlen = 0;
210    DWORD domlen = sizeof(anydomain);
211    DWORD rv;
212    char *pos;
213
214    if ((pos = strchr(username, '/'))) {
215        domain = apr_pstrndup(p, username, pos - username);
216        username = pos + 1;
217    }
218    else if ((pos = strchr(username, '\\'))) {
219        domain = apr_pstrndup(p, username, pos - username);
220        username = pos + 1;
221    }
222    else {
223        domain = NULL;
224    }
225    /* Get nothing on the first pass ... need to size the sid buffer
226     */
227    rv = LookupAccountName(domain, username, domain, &sidlen,
228                           anydomain, &domlen, &sidtype);
229    if (sidlen) {
230        /* Give it back on the second pass
231         */
232        *uid = apr_palloc(p, sidlen);
233        domlen = sizeof(anydomain);
234        rv = LookupAccountName(domain, username, *uid, &sidlen,
235                               anydomain, &domlen, &sidtype);
236    }
237    if (!sidlen || !rv) {
238        return apr_get_os_error();
239    }
240    /* There doesn't seem to be a simple way to retrieve the primary group sid
241     */
242    *gid = NULL;
243    return APR_SUCCESS;
244#endif
245}
246
247APR_DECLARE(apr_status_t) apr_uid_name_get(char **username, apr_uid_t userid,
248                                           apr_pool_t *p)
249{
250#ifdef _WIN32_WCE
251    *username = apr_pstrdup(p, "Administrator");
252    return APR_SUCCESS;
253#else
254    SID_NAME_USE type;
255    char name[MAX_PATH], domain[MAX_PATH];
256    DWORD cbname = sizeof(name), cbdomain = sizeof(domain);
257    if (!userid)
258        return APR_EINVAL;
259    if (!LookupAccountSid(NULL, userid, name, &cbname, domain, &cbdomain, &type))
260        return apr_get_os_error();
261    if (type != SidTypeUser && type != SidTypeAlias && type != SidTypeWellKnownGroup)
262        return APR_EINVAL;
263    *username = apr_pstrdup(p, name);
264    return APR_SUCCESS;
265#endif
266}
267
268APR_DECLARE(apr_status_t) apr_uid_compare(apr_uid_t left, apr_uid_t right)
269{
270    if (!left || !right)
271        return APR_EINVAL;
272#ifndef _WIN32_WCE
273    if (!IsValidSid(left) || !IsValidSid(right))
274        return APR_EINVAL;
275    if (!EqualSid(left, right))
276        return APR_EMISMATCH;
277#endif
278    return APR_SUCCESS;
279}
280
281