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