/src/pango/subprojects/glib/gio/gunixconnection.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* GIO - GLib Input, Output and Streaming Library |
2 | | * |
3 | | * Copyright © 2009 Codethink Limited |
4 | | * |
5 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
6 | | * |
7 | | * This library is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Lesser General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2.1 of the License, or (at your option) any later version. |
11 | | * |
12 | | * See the included COPYING file for more information. |
13 | | * |
14 | | * Authors: Ryan Lortie <desrt@desrt.ca> |
15 | | */ |
16 | | |
17 | | #include "config.h" |
18 | | |
19 | | #include "gunixconnection.h" |
20 | | #include "gnetworking.h" |
21 | | #include "gsocket.h" |
22 | | #include "gsocketcontrolmessage.h" |
23 | | #include "gunixcredentialsmessage.h" |
24 | | #include "gunixfdmessage.h" |
25 | | #include "glibintl.h" |
26 | | |
27 | | #include <errno.h> |
28 | | #include <string.h> |
29 | | #ifdef HAVE_UNISTD_H |
30 | | #include <unistd.h> |
31 | | #endif |
32 | | |
33 | | /** |
34 | | * GUnixConnection: |
35 | | * |
36 | | * This is the subclass of [class@Gio.SocketConnection] that is created |
37 | | * for UNIX domain sockets. |
38 | | * |
39 | | * It contains functions to do some of the UNIX socket specific |
40 | | * functionality like passing file descriptors. |
41 | | * |
42 | | * Since GLib 2.72, `GUnixConnection` is available on all platforms. It requires |
43 | | * underlying system support (such as Windows 10 with `AF_UNIX`) at run time. |
44 | | * |
45 | | * Before GLib 2.72, `<gio/gunixconnection.h>` belonged to the UNIX-specific GIO |
46 | | * interfaces, thus you had to use the `gio-unix-2.0.pc` pkg-config file when |
47 | | * using it. This is no longer necessary since GLib 2.72. |
48 | | * |
49 | | * Since: 2.22 |
50 | | */ |
51 | | |
52 | | G_DEFINE_TYPE_WITH_CODE (GUnixConnection, g_unix_connection, |
53 | | G_TYPE_SOCKET_CONNECTION, |
54 | | g_socket_connection_factory_register_type (g_define_type_id, |
55 | | G_SOCKET_FAMILY_UNIX, |
56 | | G_SOCKET_TYPE_STREAM, |
57 | | G_SOCKET_PROTOCOL_DEFAULT); |
58 | | ); |
59 | | |
60 | | /** |
61 | | * g_unix_connection_send_fd: |
62 | | * @connection: a #GUnixConnection |
63 | | * @fd: a file descriptor |
64 | | * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore. |
65 | | * @error: (nullable): #GError for error reporting, or %NULL to ignore. |
66 | | * |
67 | | * Passes a file descriptor to the receiving side of the |
68 | | * connection. The receiving end has to call g_unix_connection_receive_fd() |
69 | | * to accept the file descriptor. |
70 | | * |
71 | | * As well as sending the fd this also writes a single byte to the |
72 | | * stream, as this is required for fd passing to work on some |
73 | | * implementations. |
74 | | * |
75 | | * Returns: a %TRUE on success, %NULL on error. |
76 | | * |
77 | | * Since: 2.22 |
78 | | */ |
79 | | gboolean |
80 | | g_unix_connection_send_fd (GUnixConnection *connection, |
81 | | gint fd, |
82 | | GCancellable *cancellable, |
83 | | GError **error) |
84 | 0 | { |
85 | 0 | #ifdef G_OS_UNIX |
86 | 0 | GSocketControlMessage *scm; |
87 | 0 | GSocket *socket; |
88 | |
|
89 | 0 | g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE); |
90 | 0 | g_return_val_if_fail (fd >= 0, FALSE); |
91 | | |
92 | 0 | scm = g_unix_fd_message_new (); |
93 | |
|
94 | 0 | if (!g_unix_fd_message_append_fd (G_UNIX_FD_MESSAGE (scm), fd, error)) |
95 | 0 | { |
96 | 0 | g_object_unref (scm); |
97 | 0 | return FALSE; |
98 | 0 | } |
99 | | |
100 | 0 | g_object_get (connection, "socket", &socket, NULL); |
101 | 0 | if (g_socket_send_message (socket, NULL, NULL, 0, &scm, 1, 0, cancellable, error) != 1) |
102 | | /* XXX could it 'fail' with zero? */ |
103 | 0 | { |
104 | 0 | g_object_unref (socket); |
105 | 0 | g_object_unref (scm); |
106 | |
|
107 | 0 | return FALSE; |
108 | 0 | } |
109 | | |
110 | 0 | g_object_unref (socket); |
111 | 0 | g_object_unref (scm); |
112 | |
|
113 | 0 | return TRUE; |
114 | | #else |
115 | | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, |
116 | | _("Sending FD is not supported")); |
117 | | return FALSE; |
118 | | #endif |
119 | 0 | } |
120 | | |
121 | | /** |
122 | | * g_unix_connection_receive_fd: |
123 | | * @connection: a #GUnixConnection |
124 | | * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore |
125 | | * @error: (nullable): #GError for error reporting, or %NULL to ignore |
126 | | * |
127 | | * Receives a file descriptor from the sending end of the connection. |
128 | | * The sending end has to call g_unix_connection_send_fd() for this |
129 | | * to work. |
130 | | * |
131 | | * As well as reading the fd this also reads a single byte from the |
132 | | * stream, as this is required for fd passing to work on some |
133 | | * implementations. |
134 | | * |
135 | | * Returns: a file descriptor on success, -1 on error. |
136 | | * |
137 | | * Since: 2.22 |
138 | | **/ |
139 | | gint |
140 | | g_unix_connection_receive_fd (GUnixConnection *connection, |
141 | | GCancellable *cancellable, |
142 | | GError **error) |
143 | 0 | { |
144 | 0 | #ifdef G_OS_UNIX |
145 | 0 | GSocketControlMessage **scms; |
146 | 0 | gint *fds, nfd, fd, nscm; |
147 | 0 | GUnixFDMessage *fdmsg; |
148 | 0 | GSocket *socket; |
149 | |
|
150 | 0 | g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), -1); |
151 | | |
152 | 0 | g_object_get (connection, "socket", &socket, NULL); |
153 | 0 | if (g_socket_receive_message (socket, NULL, NULL, 0, |
154 | 0 | &scms, &nscm, NULL, cancellable, error) != 1) |
155 | | /* XXX it _could_ 'fail' with zero. */ |
156 | 0 | { |
157 | 0 | g_object_unref (socket); |
158 | |
|
159 | 0 | return -1; |
160 | 0 | } |
161 | | |
162 | 0 | g_object_unref (socket); |
163 | |
|
164 | 0 | if (nscm != 1) |
165 | 0 | { |
166 | 0 | gint i; |
167 | |
|
168 | 0 | g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, |
169 | 0 | g_dngettext (NULL, |
170 | 0 | "Expecting 1 control message, got %d", |
171 | 0 | "Expecting 1 control message, got %d", |
172 | 0 | nscm), |
173 | 0 | nscm); |
174 | |
|
175 | 0 | for (i = 0; i < nscm; i++) |
176 | 0 | g_object_unref (scms[i]); |
177 | |
|
178 | 0 | g_free (scms); |
179 | |
|
180 | 0 | return -1; |
181 | 0 | } |
182 | | |
183 | 0 | if (!G_IS_UNIX_FD_MESSAGE (scms[0])) |
184 | 0 | { |
185 | 0 | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, |
186 | 0 | _("Unexpected type of ancillary data")); |
187 | 0 | g_object_unref (scms[0]); |
188 | 0 | g_free (scms); |
189 | |
|
190 | 0 | return -1; |
191 | 0 | } |
192 | | |
193 | 0 | fdmsg = G_UNIX_FD_MESSAGE (scms[0]); |
194 | 0 | g_free (scms); |
195 | |
|
196 | 0 | fds = g_unix_fd_message_steal_fds (fdmsg, &nfd); |
197 | 0 | g_object_unref (fdmsg); |
198 | |
|
199 | 0 | if (nfd != 1) |
200 | 0 | { |
201 | 0 | gint i; |
202 | |
|
203 | 0 | g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, |
204 | 0 | g_dngettext (NULL, |
205 | 0 | "Expecting one fd, but got %d\n", |
206 | 0 | "Expecting one fd, but got %d\n", |
207 | 0 | nfd), |
208 | 0 | nfd); |
209 | |
|
210 | 0 | for (i = 0; i < nfd; i++) |
211 | 0 | close (fds[i]); |
212 | |
|
213 | 0 | g_free (fds); |
214 | |
|
215 | 0 | return -1; |
216 | 0 | } |
217 | | |
218 | 0 | fd = *fds; |
219 | 0 | g_free (fds); |
220 | |
|
221 | 0 | if (fd < 0) |
222 | 0 | { |
223 | 0 | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, |
224 | 0 | _("Received invalid fd")); |
225 | 0 | fd = -1; |
226 | 0 | } |
227 | |
|
228 | 0 | return fd; |
229 | | #else |
230 | | g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, |
231 | | _("Receiving FD is not supported")); |
232 | | return -1; |
233 | | #endif |
234 | 0 | } |
235 | | |
236 | | static void |
237 | | g_unix_connection_init (GUnixConnection *connection) |
238 | 0 | { |
239 | 0 | } |
240 | | |
241 | | static void |
242 | | g_unix_connection_class_init (GUnixConnectionClass *class) |
243 | 0 | { |
244 | 0 | } |
245 | | |
246 | | /* TODO: Other stuff we might want to add are: |
247 | | void g_unix_connection_send_fd_async (GUnixConnection *connection, |
248 | | gint fd, |
249 | | gboolean close, |
250 | | gint io_priority, |
251 | | GAsyncReadyCallback callback, |
252 | | gpointer user_data); |
253 | | gboolean g_unix_connection_send_fd_finish (GUnixConnection *connection, |
254 | | GError **error); |
255 | | |
256 | | gboolean g_unix_connection_send_fds (GUnixConnection *connection, |
257 | | gint *fds, |
258 | | gint nfds, |
259 | | GError **error); |
260 | | void g_unix_connection_send_fds_async (GUnixConnection *connection, |
261 | | gint *fds, |
262 | | gint nfds, |
263 | | gint io_priority, |
264 | | GAsyncReadyCallback callback, |
265 | | gpointer user_data); |
266 | | gboolean g_unix_connection_send_fds_finish (GUnixConnection *connection, |
267 | | GError **error); |
268 | | |
269 | | void g_unix_connection_receive_fd_async (GUnixConnection *connection, |
270 | | gint io_priority, |
271 | | GAsyncReadyCallback callback, |
272 | | gpointer user_data); |
273 | | gint g_unix_connection_receive_fd_finish (GUnixConnection *connection, |
274 | | GError **error); |
275 | | |
276 | | |
277 | | gboolean g_unix_connection_send_fake_credentials (GUnixConnection *connection, |
278 | | guint64 pid, |
279 | | guint64 uid, |
280 | | guint64 gid, |
281 | | GError **error); |
282 | | void g_unix_connection_send_fake_credentials_async (GUnixConnection *connection, |
283 | | guint64 pid, |
284 | | guint64 uid, |
285 | | guint64 gid, |
286 | | gint io_priority, |
287 | | GAsyncReadyCallback callback, |
288 | | gpointer user_data); |
289 | | gboolean g_unix_connection_send_fake_credentials_finish (GUnixConnection *connection, |
290 | | GError **error); |
291 | | |
292 | | gboolean g_unix_connection_create_pair (GUnixConnection **one, |
293 | | GUnixConnection **two, |
294 | | GError **error); |
295 | | */ |
296 | | |
297 | | |
298 | | /** |
299 | | * g_unix_connection_send_credentials: |
300 | | * @connection: A #GUnixConnection. |
301 | | * @cancellable: (nullable): A #GCancellable or %NULL. |
302 | | * @error: Return location for error or %NULL. |
303 | | * |
304 | | * Passes the credentials of the current user the receiving side |
305 | | * of the connection. The receiving end has to call |
306 | | * g_unix_connection_receive_credentials() (or similar) to accept the |
307 | | * credentials. |
308 | | * |
309 | | * As well as sending the credentials this also writes a single NUL |
310 | | * byte to the stream, as this is required for credentials passing to |
311 | | * work on some implementations. |
312 | | * |
313 | | * This method can be expected to be available on the following platforms: |
314 | | * |
315 | | * - Linux since GLib 2.26 |
316 | | * - FreeBSD since GLib 2.26 |
317 | | * - GNU/kFreeBSD since GLib 2.36 |
318 | | * - Solaris, Illumos and OpenSolaris since GLib 2.40 |
319 | | * - GNU/Hurd since GLib 2.40 |
320 | | * |
321 | | * Other ways to exchange credentials with a foreign peer includes the |
322 | | * #GUnixCredentialsMessage type and g_socket_get_credentials() function. |
323 | | * |
324 | | * Returns: %TRUE on success, %FALSE if @error is set. |
325 | | * |
326 | | * Since: 2.26 |
327 | | */ |
328 | | gboolean |
329 | | g_unix_connection_send_credentials (GUnixConnection *connection, |
330 | | GCancellable *cancellable, |
331 | | GError **error) |
332 | 0 | { |
333 | 0 | GCredentials *credentials; |
334 | 0 | GSocketControlMessage *scm; |
335 | 0 | GSocket *socket; |
336 | 0 | gboolean ret; |
337 | 0 | GOutputVector vector; |
338 | 0 | guchar nul_byte[1] = {'\0'}; |
339 | 0 | gint num_messages; |
340 | |
|
341 | 0 | g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), FALSE); |
342 | 0 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
343 | | |
344 | 0 | ret = FALSE; |
345 | |
|
346 | 0 | credentials = g_credentials_new (); |
347 | |
|
348 | 0 | vector.buffer = &nul_byte; |
349 | 0 | vector.size = 1; |
350 | |
|
351 | 0 | if (g_unix_credentials_message_is_supported ()) |
352 | 0 | { |
353 | 0 | scm = g_unix_credentials_message_new_with_credentials (credentials); |
354 | 0 | num_messages = 1; |
355 | 0 | } |
356 | 0 | else |
357 | 0 | { |
358 | 0 | scm = NULL; |
359 | 0 | num_messages = 0; |
360 | 0 | } |
361 | |
|
362 | 0 | g_object_get (connection, "socket", &socket, NULL); |
363 | 0 | if (g_socket_send_message (socket, |
364 | 0 | NULL, /* address */ |
365 | 0 | &vector, |
366 | 0 | 1, |
367 | 0 | &scm, |
368 | 0 | num_messages, |
369 | 0 | G_SOCKET_MSG_NONE, |
370 | 0 | cancellable, |
371 | 0 | error) != 1) |
372 | 0 | { |
373 | 0 | g_prefix_error (error, _("Error sending credentials: ")); |
374 | 0 | goto out; |
375 | 0 | } |
376 | | |
377 | 0 | ret = TRUE; |
378 | |
|
379 | 0 | out: |
380 | 0 | g_object_unref (socket); |
381 | 0 | if (scm != NULL) |
382 | 0 | g_object_unref (scm); |
383 | 0 | g_object_unref (credentials); |
384 | 0 | return ret; |
385 | 0 | } |
386 | | |
387 | | static void |
388 | | send_credentials_async_thread (GTask *task, |
389 | | gpointer source_object, |
390 | | gpointer task_data, |
391 | | GCancellable *cancellable) |
392 | 0 | { |
393 | 0 | GError *error = NULL; |
394 | |
|
395 | 0 | if (g_unix_connection_send_credentials (G_UNIX_CONNECTION (source_object), |
396 | 0 | cancellable, |
397 | 0 | &error)) |
398 | 0 | g_task_return_boolean (task, TRUE); |
399 | 0 | else |
400 | 0 | g_task_return_error (task, error); |
401 | 0 | g_object_unref (task); |
402 | 0 | } |
403 | | |
404 | | /** |
405 | | * g_unix_connection_send_credentials_async: |
406 | | * @connection: A #GUnixConnection. |
407 | | * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore. |
408 | | * @callback: (scope async): a #GAsyncReadyCallback |
409 | | * to call when the request is satisfied |
410 | | * @user_data: the data to pass to callback function |
411 | | * |
412 | | * Asynchronously send credentials. |
413 | | * |
414 | | * For more details, see g_unix_connection_send_credentials() which is |
415 | | * the synchronous version of this call. |
416 | | * |
417 | | * When the operation is finished, @callback will be called. You can then call |
418 | | * g_unix_connection_send_credentials_finish() to get the result of the operation. |
419 | | * |
420 | | * Since: 2.32 |
421 | | **/ |
422 | | void |
423 | | g_unix_connection_send_credentials_async (GUnixConnection *connection, |
424 | | GCancellable *cancellable, |
425 | | GAsyncReadyCallback callback, |
426 | | gpointer user_data) |
427 | 0 | { |
428 | 0 | GTask *task; |
429 | |
|
430 | 0 | task = g_task_new (connection, cancellable, callback, user_data); |
431 | 0 | g_task_set_source_tag (task, g_unix_connection_send_credentials_async); |
432 | 0 | g_task_run_in_thread (task, send_credentials_async_thread); |
433 | 0 | } |
434 | | |
435 | | /** |
436 | | * g_unix_connection_send_credentials_finish: |
437 | | * @connection: A #GUnixConnection. |
438 | | * @result: a #GAsyncResult. |
439 | | * @error: a #GError, or %NULL |
440 | | * |
441 | | * Finishes an asynchronous send credentials operation started with |
442 | | * g_unix_connection_send_credentials_async(). |
443 | | * |
444 | | * Returns: %TRUE if the operation was successful, otherwise %FALSE. |
445 | | * |
446 | | * Since: 2.32 |
447 | | **/ |
448 | | gboolean |
449 | | g_unix_connection_send_credentials_finish (GUnixConnection *connection, |
450 | | GAsyncResult *result, |
451 | | GError **error) |
452 | 0 | { |
453 | 0 | g_return_val_if_fail (g_task_is_valid (result, connection), FALSE); |
454 | | |
455 | 0 | return g_task_propagate_boolean (G_TASK (result), error); |
456 | 0 | } |
457 | | |
458 | | /** |
459 | | * g_unix_connection_receive_credentials: |
460 | | * @connection: A #GUnixConnection. |
461 | | * @cancellable: (nullable): A #GCancellable or %NULL. |
462 | | * @error: Return location for error or %NULL. |
463 | | * |
464 | | * Receives credentials from the sending end of the connection. The |
465 | | * sending end has to call g_unix_connection_send_credentials() (or |
466 | | * similar) for this to work. |
467 | | * |
468 | | * As well as reading the credentials this also reads (and discards) a |
469 | | * single byte from the stream, as this is required for credentials |
470 | | * passing to work on some implementations. |
471 | | * |
472 | | * This method can be expected to be available on the following platforms: |
473 | | * |
474 | | * - Linux since GLib 2.26 |
475 | | * - FreeBSD since GLib 2.26 |
476 | | * - GNU/kFreeBSD since GLib 2.36 |
477 | | * - Solaris, Illumos and OpenSolaris since GLib 2.40 |
478 | | * - GNU/Hurd since GLib 2.40 |
479 | | * |
480 | | * Other ways to exchange credentials with a foreign peer includes the |
481 | | * #GUnixCredentialsMessage type and g_socket_get_credentials() function. |
482 | | * |
483 | | * Returns: (transfer full): Received credentials on success (free with |
484 | | * g_object_unref()), %NULL if @error is set. |
485 | | * |
486 | | * Since: 2.26 |
487 | | */ |
488 | | GCredentials * |
489 | | g_unix_connection_receive_credentials (GUnixConnection *connection, |
490 | | GCancellable *cancellable, |
491 | | GError **error) |
492 | 0 | { |
493 | 0 | GCredentials *ret; |
494 | 0 | GSocketControlMessage **scms; |
495 | 0 | gint nscm; |
496 | 0 | GSocket *socket; |
497 | 0 | gint n; |
498 | 0 | gssize num_bytes_read; |
499 | 0 | #ifdef __linux__ |
500 | 0 | gboolean turn_off_so_passcreds; |
501 | 0 | #endif |
502 | |
|
503 | 0 | g_return_val_if_fail (G_IS_UNIX_CONNECTION (connection), NULL); |
504 | 0 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
505 | | |
506 | 0 | ret = NULL; |
507 | 0 | scms = NULL; |
508 | |
|
509 | 0 | g_object_get (connection, "socket", &socket, NULL); |
510 | | |
511 | | /* On Linux, we need to turn on SO_PASSCRED if it isn't enabled |
512 | | * already. We also need to turn it off when we're done. See |
513 | | * #617483 for more discussion. |
514 | | */ |
515 | 0 | #ifdef __linux__ |
516 | 0 | { |
517 | 0 | gint opt_val; |
518 | |
|
519 | 0 | turn_off_so_passcreds = FALSE; |
520 | 0 | opt_val = 0; |
521 | 0 | if (!g_socket_get_option (socket, |
522 | 0 | SOL_SOCKET, |
523 | 0 | SO_PASSCRED, |
524 | 0 | &opt_val, |
525 | 0 | NULL)) |
526 | 0 | { |
527 | 0 | int errsv = errno; |
528 | 0 | g_set_error (error, |
529 | 0 | G_IO_ERROR, |
530 | 0 | g_io_error_from_errno (errsv), |
531 | 0 | _("Error checking if SO_PASSCRED is enabled for socket: %s"), |
532 | 0 | g_strerror (errsv)); |
533 | 0 | goto out; |
534 | 0 | } |
535 | 0 | if (opt_val == 0) |
536 | 0 | { |
537 | 0 | if (!g_socket_set_option (socket, |
538 | 0 | SOL_SOCKET, |
539 | 0 | SO_PASSCRED, |
540 | 0 | TRUE, |
541 | 0 | NULL)) |
542 | 0 | { |
543 | 0 | int errsv = errno; |
544 | 0 | g_set_error (error, |
545 | 0 | G_IO_ERROR, |
546 | 0 | g_io_error_from_errno (errsv), |
547 | 0 | _("Error enabling SO_PASSCRED: %s"), |
548 | 0 | g_strerror (errsv)); |
549 | 0 | goto out; |
550 | 0 | } |
551 | 0 | turn_off_so_passcreds = TRUE; |
552 | 0 | } |
553 | 0 | } |
554 | 0 | #endif |
555 | | |
556 | 0 | g_type_ensure (G_TYPE_UNIX_CREDENTIALS_MESSAGE); |
557 | 0 | num_bytes_read = g_socket_receive_message (socket, |
558 | 0 | NULL, /* GSocketAddress **address */ |
559 | 0 | NULL, |
560 | 0 | 0, |
561 | 0 | &scms, |
562 | 0 | &nscm, |
563 | 0 | NULL, |
564 | 0 | cancellable, |
565 | 0 | error); |
566 | 0 | if (num_bytes_read != 1) |
567 | 0 | { |
568 | | /* Handle situation where g_socket_receive_message() returns |
569 | | * 0 bytes and not setting @error |
570 | | */ |
571 | 0 | if (num_bytes_read == 0 && error != NULL && *error == NULL) |
572 | 0 | { |
573 | 0 | g_set_error_literal (error, |
574 | 0 | G_IO_ERROR, |
575 | 0 | G_IO_ERROR_FAILED, |
576 | 0 | _("Expecting to read a single byte for receiving credentials but read zero bytes")); |
577 | 0 | } |
578 | 0 | goto out; |
579 | 0 | } |
580 | | |
581 | 0 | if (g_unix_credentials_message_is_supported () && |
582 | | /* Fall back on get_credentials if the other side didn't send the credentials */ |
583 | 0 | nscm > 0) |
584 | 0 | { |
585 | 0 | if (nscm != 1) |
586 | 0 | { |
587 | 0 | g_set_error (error, |
588 | 0 | G_IO_ERROR, |
589 | 0 | G_IO_ERROR_FAILED, |
590 | 0 | g_dngettext (NULL, |
591 | 0 | "Expecting 1 control message, got %d", |
592 | 0 | "Expecting 1 control message, got %d", |
593 | 0 | nscm), |
594 | 0 | nscm); |
595 | 0 | goto out; |
596 | 0 | } |
597 | | |
598 | 0 | if (!G_IS_UNIX_CREDENTIALS_MESSAGE (scms[0])) |
599 | 0 | { |
600 | 0 | g_set_error_literal (error, |
601 | 0 | G_IO_ERROR, |
602 | 0 | G_IO_ERROR_FAILED, |
603 | 0 | _("Unexpected type of ancillary data")); |
604 | 0 | goto out; |
605 | 0 | } |
606 | | |
607 | 0 | ret = g_unix_credentials_message_get_credentials (G_UNIX_CREDENTIALS_MESSAGE (scms[0])); |
608 | 0 | g_object_ref (ret); |
609 | 0 | } |
610 | 0 | else |
611 | 0 | { |
612 | 0 | if (nscm != 0) |
613 | 0 | { |
614 | 0 | g_set_error (error, |
615 | 0 | G_IO_ERROR, |
616 | 0 | G_IO_ERROR_FAILED, |
617 | 0 | _("Not expecting control message, but got %d"), |
618 | 0 | nscm); |
619 | 0 | goto out; |
620 | 0 | } |
621 | 0 | else |
622 | 0 | { |
623 | 0 | ret = g_socket_get_credentials (socket, error); |
624 | 0 | } |
625 | 0 | } |
626 | | |
627 | 0 | out: |
628 | |
|
629 | 0 | #ifdef __linux__ |
630 | 0 | if (turn_off_so_passcreds) |
631 | 0 | { |
632 | 0 | if (!g_socket_set_option (socket, |
633 | 0 | SOL_SOCKET, |
634 | 0 | SO_PASSCRED, |
635 | 0 | FALSE, |
636 | 0 | NULL)) |
637 | 0 | { |
638 | 0 | int errsv = errno; |
639 | 0 | g_set_error (error, |
640 | 0 | G_IO_ERROR, |
641 | 0 | g_io_error_from_errno (errsv), |
642 | 0 | _("Error while disabling SO_PASSCRED: %s"), |
643 | 0 | g_strerror (errsv)); |
644 | 0 | goto out; |
645 | 0 | } |
646 | 0 | } |
647 | 0 | #endif |
648 | | |
649 | 0 | if (scms != NULL) |
650 | 0 | { |
651 | 0 | for (n = 0; n < nscm; n++) |
652 | 0 | g_object_unref (scms[n]); |
653 | 0 | g_free (scms); |
654 | 0 | } |
655 | 0 | g_object_unref (socket); |
656 | 0 | return ret; |
657 | 0 | } |
658 | | |
659 | | static void |
660 | | receive_credentials_async_thread (GTask *task, |
661 | | gpointer source_object, |
662 | | gpointer task_data, |
663 | | GCancellable *cancellable) |
664 | 0 | { |
665 | 0 | GCredentials *creds; |
666 | 0 | GError *error = NULL; |
667 | |
|
668 | 0 | creds = g_unix_connection_receive_credentials (G_UNIX_CONNECTION (source_object), |
669 | 0 | cancellable, |
670 | 0 | &error); |
671 | 0 | if (creds) |
672 | 0 | g_task_return_pointer (task, creds, g_object_unref); |
673 | 0 | else |
674 | 0 | g_task_return_error (task, error); |
675 | 0 | g_object_unref (task); |
676 | 0 | } |
677 | | |
678 | | /** |
679 | | * g_unix_connection_receive_credentials_async: |
680 | | * @connection: A #GUnixConnection. |
681 | | * @cancellable: (nullable): optional #GCancellable object, %NULL to ignore. |
682 | | * @callback: (scope async): a #GAsyncReadyCallback |
683 | | * to call when the request is satisfied |
684 | | * @user_data: the data to pass to callback function |
685 | | * |
686 | | * Asynchronously receive credentials. |
687 | | * |
688 | | * For more details, see g_unix_connection_receive_credentials() which is |
689 | | * the synchronous version of this call. |
690 | | * |
691 | | * When the operation is finished, @callback will be called. You can then call |
692 | | * g_unix_connection_receive_credentials_finish() to get the result of the operation. |
693 | | * |
694 | | * Since: 2.32 |
695 | | **/ |
696 | | void |
697 | | g_unix_connection_receive_credentials_async (GUnixConnection *connection, |
698 | | GCancellable *cancellable, |
699 | | GAsyncReadyCallback callback, |
700 | | gpointer user_data) |
701 | 0 | { |
702 | 0 | GTask *task; |
703 | |
|
704 | 0 | task = g_task_new (connection, cancellable, callback, user_data); |
705 | 0 | g_task_set_source_tag (task, g_unix_connection_receive_credentials_async); |
706 | 0 | g_task_run_in_thread (task, receive_credentials_async_thread); |
707 | 0 | } |
708 | | |
709 | | /** |
710 | | * g_unix_connection_receive_credentials_finish: |
711 | | * @connection: A #GUnixConnection. |
712 | | * @result: a #GAsyncResult. |
713 | | * @error: a #GError, or %NULL |
714 | | * |
715 | | * Finishes an asynchronous receive credentials operation started with |
716 | | * g_unix_connection_receive_credentials_async(). |
717 | | * |
718 | | * Returns: (transfer full): a #GCredentials, or %NULL on error. |
719 | | * Free the returned object with g_object_unref(). |
720 | | * |
721 | | * Since: 2.32 |
722 | | **/ |
723 | | GCredentials * |
724 | | g_unix_connection_receive_credentials_finish (GUnixConnection *connection, |
725 | | GAsyncResult *result, |
726 | | GError **error) |
727 | 0 | { |
728 | 0 | g_return_val_if_fail (g_task_is_valid (result, connection), NULL); |
729 | | |
730 | 0 | return g_task_propagate_pointer (G_TASK (result), error); |
731 | 0 | } |