1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2018 Руслан Ижбулатов
4 * Copyright (C) 2022 Red Hat, Inc.
5 *
6 * SPDX-License-Identifier: LGPL-2.1-or-later
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General
19 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 *
21 * Author: Руслан Ижбулатов <lrn1986@gmail.com>
22 */
23
24 #include "config.h"
25
26 #include "gwin32sid.h"
27 #include "gioerror.h"
28
29 #include <sddl.h>
30
31 /**
32 * _g_win32_sid_replace: (skip)
33 * @dest: A pointer to a SID storage
34 * @src: Existing SID
35 * @error: return location for a #GError, or %NULL
36 *
37 * Creates a copy of the @src SID and puts that into @dest, after freeing
38 * existing SID in @dest (if any).
39 *
40 * The @src SID must be valid (use IsValidSid() to ensure that).
41 *
42 * Returns: TRUE on success, FALSE otherwise
43 */
44 static gboolean
45 _g_win32_sid_replace (SID **dest,
46 SID *src,
47 GError **error)
48 {
49 DWORD sid_len;
50 SID *new_sid;
51
52 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
53 g_return_val_if_fail (src != NULL, FALSE);
54 g_return_val_if_fail (dest && *dest == NULL, FALSE);
55
56 sid_len = GetLengthSid (src);
57 new_sid = g_malloc (sid_len);
58
59 if (!CopySid (sid_len, new_sid, src))
60 {
61 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
62 "Failed to copy SID");
63
64 g_free (new_sid);
65 return FALSE;
66 }
67 else
68 {
69 g_free (*dest);
70 *dest = g_steal_pointer (&new_sid);
71
72 return TRUE;
73 }
74 }
75
76 /**
77 * _g_win32_token_get_sid: (skip)
78 * @token: A handle of an access token
79 * @error: return location for a #GError, or %NULL
80 *
81 * Gets user SID of the @token and returns a copy of that SID.
82 *
83 * Returns: A newly-allocated SID, or NULL in case of an error.
84 * Free the returned SID with g_free().
85 */
86 static SID *
87 _g_win32_token_get_sid (HANDLE token,
88 GError **error)
89 {
90 TOKEN_USER *token_user = NULL;
91 DWORD n;
92 PSID psid;
93 SID *result = NULL;
94
95 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
96
97 if (!GetTokenInformation (token, TokenUser, NULL, 0, &n)
98 && GetLastError () != ERROR_INSUFFICIENT_BUFFER)
99 {
100 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
101 "Failed to GetTokenInformation");
102
103 return NULL;
104 }
105
106 token_user = g_alloca (n);
107
108 if (!GetTokenInformation (token, TokenUser, token_user, n, &n))
109 {
110 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
111 "Failed to GetTokenInformation");
112
113 return NULL;
114 }
115
116 psid = token_user->User.Sid;
117
118 if (!IsValidSid (psid))
119 {
120 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
121 "Invalid SID token");
122
123 return NULL;
124 }
125
126 _g_win32_sid_replace (&result, psid, error);
127
128 return result;
129 }
130
131 /**
132 * _g_win32_process_get_access_token_sid: (skip)
133 * @process_id: Identifier of a process to get an access token of
134 * (use 0 to get a token of the current process)
135 * @error: return location for a #GError, or %NULL
136 *
137 * Opens the process identified by @process_id and opens its token,
138 * then retrieves SID of the token user and returns a copy of that SID.
139 *
140 * Returns: A newly-allocated SID, or NULL in case of an error.
141 * Free the returned SID with g_free().
142 */
143 SID *
144 _g_win32_process_get_access_token_sid (DWORD process_id,
145 GError **error)
146 {
147 HANDLE process_handle;
148 HANDLE process_token;
149 SID *result = NULL;
150
151 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
152
153 if (process_id == 0)
154 process_handle = GetCurrentProcess ();
155 else
156 process_handle = OpenProcess (PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id);
157
158 if (process_handle == NULL)
159 {
160 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
161 "%s failed", process_id == 0 ? "GetCurrentProcess" : "OpenProcess");
162
163 return NULL;
164 }
165
166 if (!OpenProcessToken (process_handle, TOKEN_QUERY, &process_token))
167 {
168 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
169 "OpenProcessToken failed");
170
171 CloseHandle (process_handle);
172 return NULL;
173 }
174
175 result = _g_win32_token_get_sid (process_token, error);
176
177 CloseHandle (process_token);
178 CloseHandle (process_handle);
179
180 return result;
181 }
182
183 /**
184 * _g_win32_sid_to_string: (skip)
185 * @sid: a SID.
186 * @error: return location for a #GError, or %NULL
187 *
188 * Convert a SID to its string form.
189 *
190 * Returns: A newly-allocated string, or NULL in case of an error.
191 */
192 gchar *
193 _g_win32_sid_to_string (SID *sid, GError **error)
194 {
195 wchar_t *tmp = NULL;
196 char *ret;
197
198 g_return_val_if_fail (sid != NULL, NULL);
199 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
200
201 if (!ConvertSidToStringSid (sid, &tmp))
202 {
203 g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
204 "Failed to ConvertSidToString");
205 return NULL;
206 }
207
208 ret = g_utf16_to_utf8 (tmp, -1, NULL, NULL, NULL);
209 LocalFree (tmp);
210 return ret;
211 }
212
213 /**
214 * _g_win32_current_process_sid_string: (skip)
215 * @error: return location for a #GError, or %NULL
216 *
217 * Get the current process SID, as a string.
218 *
219 * Returns: A newly-allocated string, or NULL in case of an error.
220 */
221 gchar *
222 _g_win32_current_process_sid_string (GError **error)
223 {
224 SID *sid;
225 gchar *ret;
226
227 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
228
229 sid = _g_win32_process_get_access_token_sid (0, error);
230 if (!sid)
231 return NULL;
232
233 ret = _g_win32_sid_to_string (sid, error);
234 g_free (sid);
235 return ret;
236 }