Fixes to the nonce code
[platform/upstream/dbus.git] / dbus / dbus-server-socket.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-server-socket.c Server implementation for sockets
3  *
4  * Copyright (C) 2002, 2003, 2004, 2006  Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 2.1
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #include "dbus-internals.h"
25 #include "dbus-server-socket.h"
26 #include "dbus-transport-socket.h"
27 #include "dbus-connection-internal.h"
28 #include "dbus-memory.h"
29 #include "dbus-nonce.h"
30 #include "dbus-string.h"
31
32 /**
33  * @defgroup DBusServerSocket DBusServer implementations for SOCKET
34  * @ingroup  DBusInternals
35  * @brief Implementation details of DBusServer on SOCKET
36  *
37  * @{
38  */
39 /**
40  *
41  * Opaque object representing a Socket server implementation.
42  */
43 typedef struct DBusServerSocket DBusServerSocket;
44
45 /**
46  * Implementation details of DBusServerSocket. All members
47  * are private.
48  */
49 struct DBusServerSocket
50 {
51   DBusServer base;   /**< Parent class members. */
52   int n_fds;         /**< Number of active file handles */
53   int *fds;          /**< File descriptor or -1 if disconnected. */
54   DBusWatch **watch; /**< File descriptor watch. */
55   char *socket_name; /**< Name of domain socket, to unlink if appropriate */
56   DBusNonceFile *noncefile; /**< Nonce file used to authenticate clients */
57 };
58
59 static void
60 socket_finalize (DBusServer *server)
61 {
62   DBusServerSocket *socket_server = (DBusServerSocket*) server;
63   int i;
64
65   _dbus_server_finalize_base (server);
66
67   for (i = 0 ; i < socket_server->n_fds ; i++)
68     if (socket_server->watch[i])
69       {
70         _dbus_watch_unref (socket_server->watch[i]);
71         socket_server->watch[i] = NULL;
72       }
73
74   dbus_free (socket_server->fds);
75   dbus_free (socket_server->watch);
76   dbus_free (socket_server->socket_name);
77   _dbus_noncefile_delete (socket_server->noncefile, NULL);
78   dbus_free (socket_server->noncefile);
79   dbus_free (server);
80 }
81
82 /* Return value is just for memory, not other failures. */
83 static dbus_bool_t
84 handle_new_client_fd_and_unlock (DBusServer *server,
85                                  int         client_fd)
86 {
87   DBusConnection *connection;
88   DBusTransport *transport;
89   DBusNewConnectionFunction new_connection_function;
90   DBusServerSocket* socket_server;
91   void *new_connection_data;
92
93   socket_server = (DBusServerSocket*)server;
94   _dbus_verbose ("Creating new client connection with fd %d\n", client_fd);
95
96   HAVE_LOCK_CHECK (server);
97
98   if (!_dbus_set_fd_nonblocking (client_fd, NULL))
99     {
100       SERVER_UNLOCK (server);
101       return TRUE;
102     }
103
104   transport = _dbus_transport_new_for_socket (client_fd, &server->guid_hex, FALSE);
105   if (transport == NULL)
106     {
107       _dbus_close_socket (client_fd, NULL);
108       SERVER_UNLOCK (server);
109       return FALSE;
110     }
111
112   if (!_dbus_transport_set_auth_mechanisms (transport,
113                                             (const char **) server->auth_mechanisms))
114     {
115       _dbus_transport_unref (transport);
116       SERVER_UNLOCK (server);
117       return FALSE;
118     }
119
120   /* note that client_fd is now owned by the transport, and will be
121    * closed on transport disconnection/finalization
122    */
123
124   connection = _dbus_connection_new_for_transport (transport);
125   _dbus_transport_unref (transport);
126   transport = NULL; /* now under the connection lock */
127
128   if (connection == NULL)
129     {
130       SERVER_UNLOCK (server);
131       return FALSE;
132     }
133
134   /* See if someone wants to handle this new connection, self-referencing
135    * for paranoia.
136    */
137   new_connection_function = server->new_connection_function;
138   new_connection_data = server->new_connection_data;
139
140   _dbus_server_ref_unlocked (server);
141   SERVER_UNLOCK (server);
142
143   if (new_connection_function)
144     {
145       (* new_connection_function) (server, connection,
146                                    new_connection_data);
147     }
148   dbus_server_unref (server);
149
150   /* If no one grabbed a reference, the connection will die. */
151   _dbus_connection_close_if_only_one_ref (connection);
152   dbus_connection_unref (connection);
153
154   return TRUE;
155 }
156
157 static dbus_bool_t
158 socket_handle_watch (DBusWatch    *watch,
159                    unsigned int  flags,
160                    void         *data)
161 {
162   DBusServer *server = data;
163   DBusServerSocket *socket_server = data;
164
165 #ifndef DBUS_DISABLE_ASSERT
166   int i;
167   dbus_bool_t found = FALSE;
168 #endif
169
170   SERVER_LOCK (server);
171
172 #ifndef DBUS_DISABLE_ASSERT
173   for (i = 0 ; i < socket_server->n_fds ; i++)
174     {
175       if (socket_server->watch[i] == watch)
176         found = TRUE;
177     }
178   _dbus_assert (found);
179 #endif
180
181   _dbus_verbose ("Handling client connection, flags 0x%x\n", flags);
182
183   if (flags & DBUS_WATCH_READABLE)
184     {
185       int client_fd;
186       int listen_fd;
187
188       listen_fd = dbus_watch_get_socket (watch);
189
190       client_fd = _dbus_accept_with_noncefile (listen_fd, socket_server->noncefile);
191
192       if (client_fd < 0)
193         {
194           /* EINTR handled for us */
195
196           if (_dbus_get_is_errno_eagain_or_ewouldblock ())
197             _dbus_verbose ("No client available to accept after all\n");
198           else
199             _dbus_verbose ("Failed to accept a client connection: %s\n",
200                            _dbus_strerror_from_errno ());
201
202           SERVER_UNLOCK (server);
203         }
204       else
205         {
206           if (!handle_new_client_fd_and_unlock (server, client_fd))
207             _dbus_verbose ("Rejected client connection due to lack of memory\n");
208         }
209     }
210
211   if (flags & DBUS_WATCH_ERROR)
212     _dbus_verbose ("Error on server listening socket\n");
213
214   if (flags & DBUS_WATCH_HANGUP)
215     _dbus_verbose ("Hangup on server listening socket\n");
216
217   return TRUE;
218 }
219
220 static void
221 socket_disconnect (DBusServer *server)
222 {
223   DBusServerSocket *socket_server = (DBusServerSocket*) server;
224   int i;
225
226   HAVE_LOCK_CHECK (server);
227
228   for (i = 0 ; i < socket_server->n_fds ; i++)
229     {
230       if (socket_server->watch[i])
231         {
232           _dbus_server_remove_watch (server,
233                                      socket_server->watch[i]);
234           _dbus_watch_unref (socket_server->watch[i]);
235           socket_server->watch[i] = NULL;
236         }
237
238       _dbus_close_socket (socket_server->fds[i], NULL);
239       socket_server->fds[i] = -1;
240     }
241
242   if (socket_server->socket_name != NULL)
243     {
244       DBusString tmp;
245       _dbus_string_init_const (&tmp, socket_server->socket_name);
246       _dbus_delete_file (&tmp, NULL);
247     }
248
249   HAVE_LOCK_CHECK (server);
250 }
251
252 static const DBusServerVTable socket_vtable = {
253   socket_finalize,
254   socket_disconnect
255 };
256
257 /**
258  * Creates a new server listening on the given file descriptor.  The
259  * file descriptor should be nonblocking (use
260  * _dbus_set_fd_nonblocking() to make it so). The file descriptor
261  * should be listening for connections, that is, listen() should have
262  * been successfully invoked on it. The server will use accept() to
263  * accept new client connections.
264  *
265  * @param fds list of file descriptors.
266  * @param n_fds number of file descriptors
267  * @param address the server's address
268  * @param use_nonce whether to create and use a nonce for authentication
269  * @returns the new server, or #NULL if no memory.
270  *
271  */
272 DBusServer*
273 _dbus_server_new_for_socket (int              *fds,
274                              int               n_fds,
275                              const DBusString *address,
276                              DBusNonceFile    *noncefile)
277 {
278   DBusServerSocket *socket_server;
279   DBusServer *server;
280   int i;
281
282   socket_server = dbus_new0 (DBusServerSocket, 1);
283   if (socket_server == NULL)
284     return NULL;
285
286   socket_server->noncefile = noncefile;
287
288   socket_server->fds = dbus_new (int, n_fds);
289   if (!socket_server->fds)
290     goto failed_0;
291
292   socket_server->watch = dbus_new0 (DBusWatch *, n_fds);
293   if (!socket_server->watch)
294     goto failed_1;
295
296   for (i = 0 ; i < n_fds ; i++)
297     {
298       DBusWatch *watch;
299
300       watch = _dbus_watch_new (fds[i],
301                                DBUS_WATCH_READABLE,
302                                TRUE,
303                                socket_handle_watch, socket_server,
304                                NULL);
305       if (watch == NULL)
306         goto failed_2;
307
308       socket_server->n_fds++;
309       socket_server->fds[i] = fds[i];
310       socket_server->watch[i] = watch;
311     }
312
313   if (!_dbus_server_init_base (&socket_server->base,
314                                &socket_vtable, address))
315     goto failed_2;
316
317   server = (DBusServer*)socket_server;
318
319   SERVER_LOCK (server);
320
321   for (i = 0 ; i < n_fds ; i++)
322     {
323       if (!_dbus_server_add_watch (&socket_server->base,
324                                    socket_server->watch[i]))
325         {
326           int j;
327           for (j = 0 ; j < i ; j++)
328             _dbus_server_remove_watch (server,
329                                        socket_server->watch[j]);
330
331           SERVER_UNLOCK (server);
332           _dbus_server_finalize_base (&socket_server->base);
333           goto failed_2;
334         }
335     }
336
337   SERVER_UNLOCK (server);
338
339   return (DBusServer*) socket_server;
340
341  failed_3:
342   _dbus_noncefile_delete (socket_server->noncefile, NULL);
343   dbus_free (socket_server->noncefile );
344  failed_2:
345   for (i = 0 ; i < n_fds ; i++)
346     {
347       if (socket_server->watch[i] != NULL)
348         {
349           _dbus_watch_unref (socket_server->watch[i]);
350           socket_server->watch[i] = NULL;
351         }
352     }
353   dbus_free (socket_server->watch);
354
355  failed_1:
356   dbus_free (socket_server->fds);
357
358  failed_0:
359   dbus_free (socket_server);
360   return NULL;
361 }
362
363 /**
364  * Creates a new server listening on TCP.
365  * If host is NULL, it will default to localhost.
366  * If bind is NULL, it will default to the value for the host
367  * parameter, and if that is NULL, then localhost
368  * If bind is a hostname, it will be resolved and will listen
369  * on all returned addresses.
370  * If family is NULL, hostname resolution will try all address
371  * families, otherwise it can be ipv4 or ipv6 to restrict the
372  * addresses considered.
373  *
374  * @param host the hostname to report for the listen address
375  * @param bind the hostname to listen on
376  * @param port the port to listen on or 0 to let the OS choose
377  * @param family
378  * @param error location to store reason for failure.
379  * @param use_nonce whether to use a nonce for low-level authentication (nonce-tcp transport) or not (tcp transport)
380  * @returns the new server, or #NULL on failure.
381  */
382 DBusServer*
383 _dbus_server_new_for_tcp_socket (const char     *host,
384                                  const char     *bind,
385                                  const char     *port,
386                                  const char     *family,
387                                  DBusError      *error,
388                                  dbus_bool_t    use_nonce)
389 {
390   DBusServer *server;
391   int *listen_fds = NULL;
392   int nlisten_fds = 0, i;
393   DBusString address;
394   DBusString host_str;
395   DBusString port_str;
396   DBusNonceFile *noncefile;
397   
398   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
399
400   noncefile = NULL;
401
402   if (!_dbus_string_init (&address))
403     {
404       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
405       return NULL;
406     }
407
408   if (!_dbus_string_init (&port_str))
409     {
410       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
411       goto failed_0;
412     }
413
414   if (host == NULL)
415     host = "localhost";
416
417   if (port == NULL)
418     port = "0";
419
420   if (bind == NULL)
421     bind = host;
422   else if (strcmp (bind, "*") == 0)
423     bind = NULL;
424
425   nlisten_fds =_dbus_listen_tcp_socket (bind, port, family,
426                                         &port_str,
427                                         &listen_fds, error);
428   if (nlisten_fds <= 0)
429     {
430       _DBUS_ASSERT_ERROR_IS_SET(error);
431       goto failed_1;
432     }
433
434   _dbus_string_init_const (&host_str, host);
435   if (!_dbus_string_append (&address, use_nonce ? "nonce-tcp:host=" : "tcp:host=") ||
436       !_dbus_address_append_escaped (&address, &host_str) ||
437       !_dbus_string_append (&address, ",port=") ||
438       !_dbus_string_append (&address, _dbus_string_get_const_data(&port_str)))
439     {
440       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
441       goto failed_2;
442     }
443   if (family &&
444       (!_dbus_string_append (&address, ",family=") ||
445        !_dbus_string_append (&address, family)))
446     {
447       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
448       goto failed_2;
449     }
450
451   if (use_nonce)
452     {
453       noncefile = dbus_new0 (DBusNonceFile, 1);
454       if (noncefile == NULL)
455         {
456           goto failed_2;
457         }
458
459       if (!_dbus_noncefile_create (noncefile, NULL))
460           goto failed_2;
461
462       if (!_dbus_string_append (&address, ",noncefile=") ||
463           !_dbus_address_append_escaped (&address, _dbus_noncefile_get_path (noncefile)))
464         {
465           goto failed_2;
466         }
467
468     }
469
470   server = _dbus_server_new_for_socket (listen_fds, nlisten_fds, &address, noncefile);
471   if (server == NULL)
472     {
473       dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL);
474       goto failed_2;
475     }
476
477   _dbus_string_free (&port_str);
478   _dbus_string_free (&address);
479   dbus_free(listen_fds);
480
481   return server;
482
483  failed_2:
484   for (i = 0 ; i < nlisten_fds ; i++)
485     _dbus_close_socket (listen_fds[i], NULL);
486   dbus_free(listen_fds);
487
488  failed_1:
489   _dbus_string_free (&port_str);
490
491  failed_0:
492   _dbus_string_free (&address);
493
494   return NULL;
495 }
496
497 /**
498  * Tries to interpret the address entry for various socket-related
499  * addresses (well, currently only tcp and nonce-tcp).
500  *
501  * Sets error if the result is not OK.
502  *
503  * @param entry an address entry
504  * @param server_p a new DBusServer, or #NULL on failure.
505  * @param error location to store rationale for failure on bad address
506  * @returns the outcome
507  *
508  */
509 DBusServerListenResult
510 _dbus_server_listen_socket (DBusAddressEntry *entry,
511                             DBusServer      **server_p,
512                             DBusError        *error)
513 {
514   const char *method;
515
516   *server_p = NULL;
517
518   method = dbus_address_entry_get_method (entry);
519
520   if (strcmp (method, "tcp") == 0 || strcmp (method, "nonce-tcp") == 0)
521     {
522       const char *host;
523       const char *port;
524       const char *bind;
525       const char *family;
526
527       host = dbus_address_entry_get_value (entry, "host");
528       bind = dbus_address_entry_get_value (entry, "bind");
529       port = dbus_address_entry_get_value (entry, "port");
530       family = dbus_address_entry_get_value (entry, "family");
531
532       *server_p = _dbus_server_new_for_tcp_socket (host, bind, port,
533                                                    family, error, strcmp (method, "nonce-tcp") == 0 ? TRUE : FALSE);
534
535       if (*server_p)
536         {
537           _DBUS_ASSERT_ERROR_IS_CLEAR(error);
538           return DBUS_SERVER_LISTEN_OK;
539         }
540       else
541         {
542           _DBUS_ASSERT_ERROR_IS_SET(error);
543           return DBUS_SERVER_LISTEN_DID_NOT_CONNECT;
544         }
545     }
546   else
547     {
548       _DBUS_ASSERT_ERROR_IS_CLEAR(error);
549       return DBUS_SERVER_LISTEN_NOT_HANDLED;
550     }
551 }
552
553 /**
554  * This is a bad hack since it's really unix domain socket
555  * specific. Also, the function weirdly adopts ownership
556  * of the passed-in string.
557  *
558  * @param server a socket server
559  * @param filename socket filename to report/delete
560  *
561  */
562 void
563 _dbus_server_socket_own_filename (DBusServer *server,
564                                   char       *filename)
565 {
566   DBusServerSocket *socket_server = (DBusServerSocket*) server;
567
568   socket_server->socket_name = filename;
569 }
570
571
572 /** @} */
573