dbus-auth.c revision 42788678e83ec1d82f048f86aff04017b58f072e
1/* -*- mode: C; c-file-style: "gnu" -*- */
2/* dbus-auth.c Authentication
3 *
4 * Copyright (C) 2002  Red Hat Inc.
5 *
6 * Licensed under the Academic Free License version 1.2
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 *
22 */
23#include "dbus-auth.h"
24#include "dbus-string.h"
25#include "dbus-list.h"
26#include "dbus-internals.h"
27
28/* See doc/dbus-sasl-profile.txt */
29
30/**
31 * @defgroup DBusAuth Authentication
32 * @ingroup  DBusInternals
33 * @brief DBusAuth object
34 *
35 * DBusAuth manages the authentication negotiation when a connection
36 * is first established, and also manage any encryption used over a
37 * connection.
38 *
39 * The file doc/dbus-sasl-profile.txt documents the network protocol
40 * used for authentication.
41 */
42
43/**
44 * @defgroup DBusAuthInternals Authentication implementation details
45 * @ingroup  DBusInternals
46 * @brief DBusAuth implementation details
47 *
48 * Private details of authentication code.
49 *
50 * @{
51 */
52
53/**
54 * Processes a command. Returns whether we had enough memory to
55 * complete the operation.
56 */
57typedef dbus_bool_t (* DBusProcessAuthCommandFunction) (DBusAuth         *auth,
58                                                        const DBusString *command,
59                                                        const DBusString *args);
60
61typedef struct
62{
63  const char *command;
64  DBusProcessAuthCommandFunction func;
65} DBusAuthCommandHandler;
66
67/**
68 * This function appends an initial client response to the given string
69 */
70typedef dbus_bool_t (* DBusInitialResponseFunction)  (DBusAuth         *auth,
71                                                      DBusString       *response);
72
73/**
74 * This function processes a block of data received from the peer.
75 * i.e. handles a DATA command.
76 */
77typedef dbus_bool_t (* DBusAuthDataFunction)     (DBusAuth         *auth,
78                                                  const DBusString *data);
79
80/**
81 * This function encodes a block of data from the peer.
82 */
83typedef dbus_bool_t (* DBusAuthEncodeFunction)   (DBusAuth         *auth,
84                                                  const DBusString *data,
85                                                  DBusString       *encoded);
86
87/**
88 * This function decodes a block of data from the peer.
89 */
90typedef dbus_bool_t (* DBusAuthDecodeFunction)   (DBusAuth         *auth,
91                                                  const DBusString *data,
92                                                  DBusString       *decoded);
93
94/**
95 * This function is called when the mechanism is abandoned.
96 */
97typedef void        (* DBusAuthShutdownFunction) (DBusAuth       *auth);
98
99typedef struct
100{
101  const char *mechanism;
102  DBusAuthDataFunction server_data_func;
103  DBusAuthEncodeFunction server_encode_func;
104  DBusAuthDecodeFunction server_decode_func;
105  DBusAuthShutdownFunction server_shutdown_func;
106  DBusInitialResponseFunction client_initial_response_func;
107  DBusAuthDataFunction client_data_func;
108  DBusAuthEncodeFunction client_encode_func;
109  DBusAuthDecodeFunction client_decode_func;
110  DBusAuthShutdownFunction client_shutdown_func;
111} DBusAuthMechanismHandler;
112
113/**
114 * Internal members of DBusAuth.
115 */
116struct DBusAuth
117{
118  int refcount;           /**< reference count */
119
120  DBusString incoming;    /**< Incoming data buffer */
121  DBusString outgoing;    /**< Outgoing data buffer */
122
123  const DBusAuthCommandHandler *handlers; /**< Handlers for commands */
124
125  const DBusAuthMechanismHandler *mech;   /**< Current auth mechanism */
126
127  DBusString identity;                   /**< Current identity we're authorizing
128                                          *   as.
129                                          */
130
131  DBusCredentials credentials;      /**< Credentials, fields may be -1 */
132
133  DBusCredentials authorized_identity; /**< Credentials that are authorized */
134
135  unsigned int needed_memory : 1;   /**< We needed memory to continue since last
136                                     * successful getting something done
137                                     */
138  unsigned int need_disconnect : 1; /**< We've given up, time to disconnect */
139  unsigned int authenticated : 1;   /**< We are authenticated */
140  unsigned int authenticated_pending_output : 1; /**< Authenticated once we clear outgoing buffer */
141  unsigned int authenticated_pending_begin : 1;  /**< Authenticated once we get BEGIN */
142  unsigned int already_got_mechanisms : 1;       /**< Client already got mech list */
143  unsigned int already_asked_for_initial_response : 1; /**< Already sent a blank challenge to get an initial response */
144};
145
146typedef struct
147{
148  DBusAuth base;
149
150  DBusList *mechs_to_try;
151
152} DBusAuthClient;
153
154typedef struct
155{
156  DBusAuth base;
157
158} DBusAuthServer;
159
160static dbus_bool_t process_auth         (DBusAuth         *auth,
161                                         const DBusString *command,
162                                         const DBusString *args);
163static dbus_bool_t process_cancel       (DBusAuth         *auth,
164                                         const DBusString *command,
165                                         const DBusString *args);
166static dbus_bool_t process_begin        (DBusAuth         *auth,
167                                         const DBusString *command,
168                                         const DBusString *args);
169static dbus_bool_t process_data_server  (DBusAuth         *auth,
170                                         const DBusString *command,
171                                         const DBusString *args);
172static dbus_bool_t process_error_server (DBusAuth         *auth,
173                                         const DBusString *command,
174                                         const DBusString *args);
175static dbus_bool_t process_mechanisms   (DBusAuth         *auth,
176                                         const DBusString *command,
177                                         const DBusString *args);
178static dbus_bool_t process_rejected     (DBusAuth         *auth,
179                                         const DBusString *command,
180                                         const DBusString *args);
181static dbus_bool_t process_ok           (DBusAuth         *auth,
182                                         const DBusString *command,
183                                         const DBusString *args);
184static dbus_bool_t process_data_client  (DBusAuth         *auth,
185                                         const DBusString *command,
186                                         const DBusString *args);
187static dbus_bool_t process_error_client (DBusAuth         *auth,
188                                         const DBusString *command,
189                                         const DBusString *args);
190
191
192static dbus_bool_t client_try_next_mechanism (DBusAuth *auth);
193
194
195static DBusAuthCommandHandler
196server_handlers[] = {
197  { "AUTH", process_auth },
198  { "CANCEL", process_cancel },
199  { "BEGIN", process_begin },
200  { "DATA", process_data_server },
201  { "ERROR", process_error_server },
202  { NULL, NULL }
203};
204
205static DBusAuthCommandHandler
206client_handlers[] = {
207  { "MECHANISMS", process_mechanisms },
208  { "REJECTED", process_rejected },
209  { "OK", process_ok },
210  { "DATA", process_data_client },
211  { "ERROR", process_error_client },
212  { NULL, NULL }
213};
214
215/**
216 * @param auth the auth conversation
217 * @returns #TRUE if the conversation is the server side
218 */
219#define DBUS_AUTH_IS_SERVER(auth) ((auth)->handlers == server_handlers)
220/**
221 * @param auth the auth conversation
222 * @returns #TRUE if the conversation is the client side
223 */
224#define DBUS_AUTH_IS_CLIENT(auth) ((auth)->handlers == client_handlers)
225/**
226 * @param auth the auth conversation
227 * @returns auth cast to DBusAuthClient
228 */
229#define DBUS_AUTH_CLIENT(auth)    ((DBusAuthClient*)(auth))
230/**
231 * @param auth the auth conversation
232 * @returns auth cast to DBusAuthServer
233 */
234#define DBUS_AUTH_SERVER(auth)    ((DBusAuthServer*)(auth))
235
236static DBusAuth*
237_dbus_auth_new (int size)
238{
239  DBusAuth *auth;
240
241  auth = dbus_malloc0 (size);
242  if (auth == NULL)
243    return NULL;
244
245  auth->refcount = 1;
246
247  auth->credentials.pid = -1;
248  auth->credentials.uid = -1;
249  auth->credentials.gid = -1;
250
251  auth->authorized_identity.pid = -1;
252  auth->authorized_identity.uid = -1;
253  auth->authorized_identity.gid = -1;
254
255  /* note that we don't use the max string length feature,
256   * because you can't use that feature if you're going to
257   * try to recover from out-of-memory (it creates
258   * what looks like unrecoverable inability to alloc
259   * more space in the string). But we do handle
260   * overlong buffers in _dbus_auth_do_work().
261   */
262
263  if (!_dbus_string_init (&auth->incoming, _DBUS_INT_MAX))
264    {
265      dbus_free (auth);
266      return NULL;
267    }
268
269  if (!_dbus_string_init (&auth->outgoing, _DBUS_INT_MAX))
270    {
271      _dbus_string_free (&auth->incoming);
272      dbus_free (auth);
273      return NULL;
274    }
275
276  if (!_dbus_string_init (&auth->identity, _DBUS_INT_MAX))
277    {
278      _dbus_string_free (&auth->incoming);
279      _dbus_string_free (&auth->outgoing);
280      dbus_free (auth);
281      return NULL;
282    }
283
284  return auth;
285}
286
287static DBusAuthState
288get_state (DBusAuth *auth)
289{
290  if (auth->need_disconnect)
291    return DBUS_AUTH_STATE_NEED_DISCONNECT;
292  else if (auth->authenticated)
293    {
294      if (_dbus_string_get_length (&auth->incoming) > 0)
295        return DBUS_AUTH_STATE_AUTHENTICATED_WITH_UNUSED_BYTES;
296      else
297        return DBUS_AUTH_STATE_AUTHENTICATED;
298    }
299  else if (auth->needed_memory)
300    return DBUS_AUTH_STATE_WAITING_FOR_MEMORY;
301  else if (_dbus_string_get_length (&auth->outgoing) > 0)
302    return DBUS_AUTH_STATE_HAVE_BYTES_TO_SEND;
303  else
304    return DBUS_AUTH_STATE_WAITING_FOR_INPUT;
305}
306
307static void
308shutdown_mech (DBusAuth *auth)
309{
310  /* Cancel any auth */
311  auth->authenticated_pending_begin = FALSE;
312  auth->authenticated = FALSE;
313  auth->already_asked_for_initial_response = FALSE;
314  _dbus_string_set_length (&auth->identity, 0);
315  auth->authorized_identity.pid = -1;
316  auth->authorized_identity.uid = -1;
317  auth->authorized_identity.gid = -1;
318
319  if (auth->mech != NULL)
320    {
321      _dbus_verbose ("Shutting down mechanism %s\n",
322                     auth->mech->mechanism);
323
324      if (DBUS_AUTH_IS_CLIENT (auth))
325        (* auth->mech->client_shutdown_func) (auth);
326      else
327        (* auth->mech->server_shutdown_func) (auth);
328
329      auth->mech = NULL;
330    }
331}
332
333static dbus_bool_t
334handle_server_data_stupid_test_mech (DBusAuth         *auth,
335                                     const DBusString *data)
336{
337  if (!_dbus_string_append (&auth->outgoing,
338                            "OK\r\n"))
339    return FALSE;
340
341  auth->authenticated_pending_begin = TRUE;
342
343  return TRUE;
344}
345
346static void
347handle_server_shutdown_stupid_test_mech (DBusAuth *auth)
348{
349
350}
351
352static dbus_bool_t
353handle_client_data_stupid_test_mech (DBusAuth         *auth,
354                                     const DBusString *data)
355{
356
357  return TRUE;
358}
359
360static void
361handle_client_shutdown_stupid_test_mech (DBusAuth *auth)
362{
363
364}
365
366/* the stupid test mech is a base64-encoded string;
367 * all the inefficiency, none of the security!
368 */
369static dbus_bool_t
370handle_encode_stupid_test_mech (DBusAuth         *auth,
371                                const DBusString *plaintext,
372                                DBusString       *encoded)
373{
374  if (!_dbus_string_base64_encode (plaintext, 0, encoded,
375                                   _dbus_string_get_length (encoded)))
376    return FALSE;
377
378  return TRUE;
379}
380
381static dbus_bool_t
382handle_decode_stupid_test_mech (DBusAuth         *auth,
383                                const DBusString *encoded,
384                                DBusString       *plaintext)
385{
386  if (!_dbus_string_base64_decode (encoded, 0, plaintext,
387                                   _dbus_string_get_length (plaintext)))
388    return FALSE;
389
390  return TRUE;
391}
392
393static dbus_bool_t
394do_rejection (DBusAuth *auth)
395{
396  if (_dbus_string_append (&auth->outgoing,
397                           "REJECTED\r\n"))
398    {
399      shutdown_mech (auth);
400      _dbus_verbose ("rejected client auth\n");
401      return TRUE;
402    }
403  else
404    return FALSE;
405}
406
407static dbus_bool_t
408handle_server_data_external_mech (DBusAuth         *auth,
409                                  const DBusString *data)
410{
411  DBusCredentials desired_identity;
412
413  if (auth->credentials.uid < 0)
414    {
415      _dbus_verbose ("no credentials, mechanism EXTERNAL can't authenticate\n");
416      return do_rejection (auth);
417    }
418
419  if (_dbus_string_get_length (data) > 0)
420    {
421      if (_dbus_string_get_length (&auth->identity) > 0)
422        {
423          /* Tried to send two auth identities, wtf */
424          return do_rejection (auth);
425        }
426      else
427        {
428          /* this is our auth identity */
429          if (!_dbus_string_copy (data, 0, &auth->identity, 0))
430            return FALSE;
431        }
432    }
433
434  /* Poke client for an auth identity, if none given */
435  if (_dbus_string_get_length (&auth->identity) == 0 &&
436      !auth->already_asked_for_initial_response)
437    {
438      if (_dbus_string_append (&auth->outgoing,
439                               "DATA\r\n"))
440        {
441          _dbus_verbose ("sending empty challenge asking client for auth identity\n");
442          auth->already_asked_for_initial_response = TRUE;
443          return TRUE;
444        }
445      else
446        return FALSE;
447    }
448
449  desired_identity.pid = -1;
450  desired_identity.uid = -1;
451  desired_identity.gid = -1;
452
453  /* If auth->identity is still empty here, then client
454   * responded with an empty string after we poked it for
455   * an initial response. This means to try to auth the
456   * identity provided in the credentials.
457   */
458  if (_dbus_string_get_length (&auth->identity) == 0)
459    {
460      desired_identity.uid = auth->credentials.uid;
461    }
462  else
463    {
464      if (!_dbus_credentials_from_uid_string (&auth->identity,
465                                              &desired_identity))
466        return do_rejection (auth);
467    }
468
469  if (desired_identity.uid < 0)
470    {
471      _dbus_verbose ("desired UID %d is no good\n", desired_identity.uid);
472      return do_rejection (auth);
473    }
474
475  if (_dbus_credentials_match (&auth->credentials,
476                               &desired_identity))
477    {
478      /* client has authenticated */
479      _dbus_verbose ("authenticated client with UID %d matching socket credentials UID %d\n",
480                     desired_identity.uid,
481                     auth->credentials.uid);
482
483      if (!_dbus_string_append (&auth->outgoing,
484                                "OK\r\n"))
485        return FALSE;
486
487      auth->authorized_identity.uid = desired_identity.uid;
488
489      auth->authenticated_pending_begin = TRUE;
490
491      return TRUE;
492    }
493  else
494    {
495      return do_rejection (auth);
496    }
497}
498
499static void
500handle_server_shutdown_external_mech (DBusAuth *auth)
501{
502
503}
504
505static dbus_bool_t
506handle_client_initial_response_external_mech (DBusAuth         *auth,
507                                              DBusString       *response)
508{
509  /* We always append our UID as an initial response, so the server
510   * doesn't have to send back an empty challenge to check whether we
511   * want to specify an identity. i.e. this avoids a round trip that
512   * the spec for the EXTERNAL mechanism otherwise requires.
513   */
514  DBusString plaintext;
515
516  if (!_dbus_string_init (&plaintext, _DBUS_INT_MAX))
517    return FALSE;
518
519  if (!_dbus_string_append_our_uid (&plaintext))
520    goto failed;
521
522  if (!_dbus_string_base64_encode (&plaintext, 0,
523                                   response,
524                                   _dbus_string_get_length (response)))
525    goto failed;
526
527  _dbus_string_free (&plaintext);
528
529  return TRUE;
530
531 failed:
532  _dbus_string_free (&plaintext);
533  return FALSE;
534}
535
536static dbus_bool_t
537handle_client_data_external_mech (DBusAuth         *auth,
538                                  const DBusString *data)
539{
540
541  return TRUE;
542}
543
544static void
545handle_client_shutdown_external_mech (DBusAuth *auth)
546{
547
548}
549
550/* Put mechanisms here in order of preference.
551 * What I eventually want to have is:
552 *
553 *  - a mechanism that checks UNIX domain socket credentials
554 *  - a simple magic cookie mechanism like X11 or ICE
555 *  - mechanisms that chain to Cyrus SASL, so we can use anything it
556 *    offers such as Kerberos, X509, whatever.
557 *
558 */
559static const DBusAuthMechanismHandler
560all_mechanisms[] = {
561  { "EXTERNAL",
562    handle_server_data_external_mech,
563    NULL, NULL,
564    handle_server_shutdown_external_mech,
565    handle_client_initial_response_external_mech,
566    handle_client_data_external_mech,
567    NULL, NULL,
568    handle_client_shutdown_external_mech },
569  /* Obviously this has to die for production use */
570  { "DBUS_STUPID_TEST_MECH",
571    handle_server_data_stupid_test_mech,
572    handle_encode_stupid_test_mech,
573    handle_decode_stupid_test_mech,
574    handle_server_shutdown_stupid_test_mech,
575    NULL,
576    handle_client_data_stupid_test_mech,
577    handle_encode_stupid_test_mech,
578    handle_decode_stupid_test_mech,
579    handle_client_shutdown_stupid_test_mech },
580  { NULL, NULL }
581};
582
583static const DBusAuthMechanismHandler*
584find_mech (const DBusString *name)
585{
586  int i;
587
588  i = 0;
589  while (all_mechanisms[i].mechanism != NULL)
590    {
591      if (_dbus_string_equal_c_str (name,
592                                    all_mechanisms[i].mechanism))
593
594        return &all_mechanisms[i];
595
596      ++i;
597    }
598
599  return NULL;
600}
601
602static dbus_bool_t
603send_mechanisms (DBusAuth *auth)
604{
605  DBusString command;
606  int i;
607
608  if (!_dbus_string_init (&command, _DBUS_INT_MAX))
609    return FALSE;
610
611  if (!_dbus_string_append (&command,
612                            "MECHANISMS"))
613    goto nomem;
614
615  i = 0;
616  while (all_mechanisms[i].mechanism != NULL)
617    {
618      if (!_dbus_string_append (&command,
619                                " "))
620        goto nomem;
621
622      if (!_dbus_string_append (&command,
623                                all_mechanisms[i].mechanism))
624        goto nomem;
625
626      ++i;
627    }
628
629  if (!_dbus_string_append (&command, "\r\n"))
630    goto nomem;
631
632  if (!_dbus_string_copy (&command, 0, &auth->outgoing,
633                          _dbus_string_get_length (&auth->outgoing)))
634    goto nomem;
635
636  return TRUE;
637
638 nomem:
639  _dbus_string_free (&command);
640  return FALSE;
641}
642
643static dbus_bool_t
644process_auth (DBusAuth         *auth,
645              const DBusString *command,
646              const DBusString *args)
647{
648  if (auth->mech)
649    {
650      /* We are already using a mechanism, client is on crack */
651      if (!_dbus_string_append (&auth->outgoing,
652                                "ERROR \"Sent AUTH while another AUTH in progress\"\r\n"))
653        return FALSE;
654
655      return TRUE;
656    }
657  else if (_dbus_string_get_length (args) == 0)
658    {
659      /* No args to the auth, send mechanisms */
660      if (!send_mechanisms (auth))
661        return FALSE;
662
663      return TRUE;
664    }
665  else
666    {
667      int i;
668      DBusString mech;
669      DBusString base64_response;
670      DBusString decoded_response;
671
672      _dbus_string_find_blank (args, 0, &i);
673
674      if (!_dbus_string_init (&mech, _DBUS_INT_MAX))
675        return FALSE;
676
677      if (!_dbus_string_init (&base64_response, _DBUS_INT_MAX))
678        {
679          _dbus_string_free (&mech);
680          return FALSE;
681        }
682
683      if (!_dbus_string_init (&decoded_response, _DBUS_INT_MAX))
684        {
685          _dbus_string_free (&mech);
686          _dbus_string_free (&base64_response);
687          return FALSE;
688        }
689
690      if (!_dbus_string_copy_len (args, 0, i, &mech, 0))
691        goto failed;
692
693      if (!_dbus_string_copy (args, i, &base64_response, 0))
694        goto failed;
695
696      if (!_dbus_string_base64_decode (&base64_response, 0,
697                                       &decoded_response, 0))
698        goto failed;
699
700      auth->mech = find_mech (&mech);
701      if (auth->mech != NULL)
702        {
703          _dbus_verbose ("Trying mechanism %s with initial response of %d bytes\n",
704                         auth->mech->mechanism,
705                         _dbus_string_get_length (&decoded_response));
706
707          if (!(* auth->mech->server_data_func) (auth,
708                                                 &decoded_response))
709            goto failed;
710        }
711      else
712        {
713          /* Unsupported mechanism */
714          if (!send_mechanisms (auth))
715            return FALSE;
716        }
717
718      _dbus_string_free (&mech);
719      _dbus_string_free (&base64_response);
720      _dbus_string_free (&decoded_response);
721
722      return TRUE;
723
724    failed:
725      auth->mech = NULL;
726      _dbus_string_free (&mech);
727      _dbus_string_free (&base64_response);
728      _dbus_string_free (&decoded_response);
729      return FALSE;
730    }
731}
732
733static dbus_bool_t
734process_cancel (DBusAuth         *auth,
735                const DBusString *command,
736                const DBusString *args)
737{
738  shutdown_mech (auth);
739
740  return TRUE;
741}
742
743static dbus_bool_t
744process_begin (DBusAuth         *auth,
745               const DBusString *command,
746               const DBusString *args)
747{
748  if (auth->authenticated_pending_begin)
749    auth->authenticated = TRUE;
750  else
751    {
752      auth->need_disconnect = TRUE; /* client trying to send data before auth,
753                                     * kick it
754                                     */
755      shutdown_mech (auth);
756    }
757
758  return TRUE;
759}
760
761static dbus_bool_t
762process_data_server (DBusAuth         *auth,
763                     const DBusString *command,
764                     const DBusString *args)
765{
766  if (auth->mech != NULL)
767    {
768      DBusString decoded;
769
770      if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
771        return FALSE;
772
773      if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
774        {
775          _dbus_string_free (&decoded);
776          return FALSE;
777        }
778
779      if (!(* auth->mech->server_data_func) (auth, &decoded))
780        {
781          _dbus_string_free (&decoded);
782          return FALSE;
783        }
784
785      _dbus_string_free (&decoded);
786    }
787  else
788    {
789      if (!_dbus_string_append (&auth->outgoing,
790                                "ERROR \"Not currently in an auth conversation\"\r\n"))
791        return FALSE;
792    }
793
794  return TRUE;
795}
796
797static dbus_bool_t
798process_error_server (DBusAuth         *auth,
799                      const DBusString *command,
800                      const DBusString *args)
801{
802
803  return TRUE;
804}
805
806/* return FALSE if no memory, TRUE if all OK */
807static dbus_bool_t
808get_word (const DBusString *str,
809          int              *start,
810          DBusString       *word)
811{
812  int i;
813
814  _dbus_string_skip_blank (str, *start, start);
815  _dbus_string_find_blank (str, *start, &i);
816
817  if (i > *start)
818    {
819      if (!_dbus_string_copy_len (str, *start, i, word, 0))
820        return FALSE;
821
822      *start = i;
823    }
824
825  return TRUE;
826}
827
828static dbus_bool_t
829process_mechanisms (DBusAuth         *auth,
830                    const DBusString *command,
831                    const DBusString *args)
832{
833  int next;
834  int len;
835
836  if (auth->already_got_mechanisms)
837    return TRUE;
838
839  len = _dbus_string_get_length (args);
840
841  next = 0;
842  while (next < len)
843    {
844      DBusString m;
845      const DBusAuthMechanismHandler *mech;
846
847      if (!_dbus_string_init (&m, _DBUS_INT_MAX))
848        goto nomem;
849
850      if (!get_word (args, &next, &m))
851        goto nomem;
852
853      mech = find_mech (&m);
854
855      if (mech != NULL)
856        {
857          /* FIXME right now we try mechanisms in the order
858           * the server lists them; should we do them in
859           * some more deterministic order?
860           *
861           * Probably in all_mechanisms order, our order of
862           * preference. Of course when the server is us,
863           * it lists things in that order anyhow.
864           */
865
866          _dbus_verbose ("Adding mechanism %s to list we will try\n",
867                         mech->mechanism);
868
869          if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
870                                  (void*) mech))
871            goto nomem;
872        }
873      else
874        {
875          const char *s;
876
877          _dbus_string_get_const_data (&m, &s);
878          _dbus_verbose ("Server offered mechanism \"%s\" that we don't know how to use\n",
879                         s);
880        }
881
882      _dbus_string_free (&m);
883    }
884
885  auth->already_got_mechanisms = TRUE;
886
887  return TRUE;
888
889 nomem:
890  _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
891
892  return FALSE;
893}
894
895static dbus_bool_t
896client_try_next_mechanism (DBusAuth *auth)
897{
898  const DBusAuthMechanismHandler *mech;
899  DBusString auth_command;
900
901  if (DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
902    return FALSE;
903
904  mech = DBUS_AUTH_CLIENT (auth)->mechs_to_try->data;
905
906  if (!_dbus_string_init (&auth_command, _DBUS_INT_MAX))
907    return FALSE;
908
909  if (!_dbus_string_append (&auth_command,
910                            "AUTH "))
911    {
912      _dbus_string_free (&auth_command);
913      return FALSE;
914    }
915
916  if (!_dbus_string_append (&auth_command,
917                            mech->mechanism))
918    {
919      _dbus_string_free (&auth_command);
920      return FALSE;
921    }
922
923  if (mech->client_initial_response_func != NULL)
924    {
925      if (!_dbus_string_append (&auth_command, " "))
926        {
927          _dbus_string_free (&auth_command);
928          return FALSE;
929        }
930
931      if (!(* mech->client_initial_response_func) (auth, &auth_command))
932        {
933          _dbus_string_free (&auth_command);
934          return FALSE;
935        }
936    }
937
938  if (!_dbus_string_append (&auth_command,
939                            "\r\n"))
940    {
941      _dbus_string_free (&auth_command);
942      return FALSE;
943    }
944
945  if (!_dbus_string_copy (&auth_command, 0,
946                          &auth->outgoing,
947                          _dbus_string_get_length (&auth->outgoing)))
948    {
949      _dbus_string_free (&auth_command);
950      return FALSE;
951    }
952
953  auth->mech = mech;
954  _dbus_list_pop_first (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
955
956  _dbus_verbose ("Trying mechanism %s\n",
957                 auth->mech->mechanism);
958
959  return TRUE;
960}
961
962static dbus_bool_t
963process_rejected (DBusAuth         *auth,
964                  const DBusString *command,
965                  const DBusString *args)
966{
967  shutdown_mech (auth);
968
969  if (!auth->already_got_mechanisms)
970    {
971      /* Ask for mechanisms */
972      if (!_dbus_string_append (&auth->outgoing,
973                                "AUTH\r\n"))
974        return FALSE;
975    }
976  else if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
977    {
978      client_try_next_mechanism (auth);
979    }
980  else
981    {
982      /* Give up */
983      auth->need_disconnect = TRUE;
984    }
985
986  return TRUE;
987}
988
989static dbus_bool_t
990process_ok (DBusAuth         *auth,
991            const DBusString *command,
992            const DBusString *args)
993{
994  if (!_dbus_string_append (&auth->outgoing,
995                            "BEGIN\r\n"))
996    return FALSE;
997
998  auth->authenticated_pending_output = TRUE;
999
1000  return TRUE;
1001}
1002
1003
1004static dbus_bool_t
1005process_data_client (DBusAuth         *auth,
1006                     const DBusString *command,
1007                     const DBusString *args)
1008{
1009  if (auth->mech != NULL)
1010    {
1011      DBusString decoded;
1012
1013      if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
1014        return FALSE;
1015
1016      if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
1017        {
1018          _dbus_string_free (&decoded);
1019          return FALSE;
1020        }
1021
1022      if (!(* auth->mech->client_data_func) (auth, &decoded))
1023        {
1024          _dbus_string_free (&decoded);
1025          return FALSE;
1026        }
1027
1028      _dbus_string_free (&decoded);
1029    }
1030  else
1031    {
1032      if (!_dbus_string_append (&auth->outgoing,
1033                                "ERROR \"Got DATA when not in an auth exchange\"\r\n"))
1034        return FALSE;
1035    }
1036
1037  return TRUE;
1038}
1039
1040static dbus_bool_t
1041process_error_client (DBusAuth         *auth,
1042                      const DBusString *command,
1043                      const DBusString *args)
1044{
1045  return TRUE;
1046}
1047
1048static dbus_bool_t
1049process_unknown (DBusAuth         *auth,
1050                 const DBusString *command,
1051                 const DBusString *args)
1052{
1053  if (!_dbus_string_append (&auth->outgoing,
1054                            "ERROR \"Unknown command\"\r\n"))
1055    return FALSE;
1056
1057  return TRUE;
1058}
1059
1060/* returns whether to call it again right away */
1061static dbus_bool_t
1062process_command (DBusAuth *auth)
1063{
1064  DBusString command;
1065  DBusString args;
1066  int eol;
1067  int i, j;
1068  dbus_bool_t retval;
1069
1070  retval = FALSE;
1071
1072  eol = 0;
1073  if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
1074    return FALSE;
1075
1076  if (!_dbus_string_init (&command, _DBUS_INT_MAX))
1077    {
1078      auth->needed_memory = TRUE;
1079      return FALSE;
1080    }
1081
1082  if (!_dbus_string_init (&args, _DBUS_INT_MAX))
1083    {
1084      auth->needed_memory = TRUE;
1085      return FALSE;
1086    }
1087
1088  if (eol > _DBUS_ONE_MEGABYTE)
1089    {
1090      /* This is a giant line, someone is trying to hose us. */
1091      if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command too long\"\r\n"))
1092        goto out;
1093      else
1094        goto next_command;
1095    }
1096
1097  if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0))
1098    goto out;
1099
1100  if (!_dbus_string_validate_ascii (&command, 0,
1101                                    _dbus_string_get_length (&command)))
1102    {
1103      _dbus_verbose ("Command contained non-ASCII chars or embedded nul\n");
1104      if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command contained non-ASCII\"\r\n"))
1105        goto out;
1106      else
1107        goto next_command;
1108    }
1109
1110  {
1111    const char *q;
1112    _dbus_string_get_const_data (&command, &q);
1113    _dbus_verbose ("got command \"%s\"\n", q);
1114  }
1115
1116  _dbus_string_find_blank (&command, 0, &i);
1117  _dbus_string_skip_blank (&command, i, &j);
1118
1119  if (j > i)
1120    _dbus_string_delete (&command, i, j - i);
1121
1122  if (!_dbus_string_move (&command, i, &args, 0))
1123    goto out;
1124
1125  i = 0;
1126  while (auth->handlers[i].command != NULL)
1127    {
1128      if (_dbus_string_equal_c_str (&command,
1129                                    auth->handlers[i].command))
1130        {
1131          _dbus_verbose ("Processing auth command %s\n",
1132                         auth->handlers[i].command);
1133
1134          if (!(* auth->handlers[i].func) (auth, &command, &args))
1135            goto out;
1136
1137          break;
1138        }
1139      ++i;
1140    }
1141
1142  if (auth->handlers[i].command == NULL)
1143    {
1144      if (!process_unknown (auth, &command, &args))
1145        goto out;
1146    }
1147
1148 next_command:
1149
1150  /* We've succeeded in processing the whole command so drop it out
1151   * of the incoming buffer and return TRUE to try another command.
1152   */
1153
1154  _dbus_string_delete (&auth->incoming, 0, eol);
1155
1156  /* kill the \r\n */
1157  _dbus_string_delete (&auth->incoming, 0, 2);
1158
1159  retval = TRUE;
1160
1161 out:
1162  _dbus_string_free (&args);
1163  _dbus_string_free (&command);
1164
1165  if (!retval)
1166    auth->needed_memory = TRUE;
1167  else
1168    auth->needed_memory = FALSE;
1169
1170  return retval;
1171}
1172
1173
1174/** @} */
1175
1176/**
1177 * @addtogroup DBusAuth
1178 * @{
1179 */
1180
1181/**
1182 * Creates a new auth conversation object for the server side.
1183 * See doc/dbus-sasl-profile.txt for full details on what
1184 * this object does.
1185 *
1186 * @returns the new object or #NULL if no memory
1187 */
1188DBusAuth*
1189_dbus_auth_server_new (void)
1190{
1191  DBusAuth *auth;
1192
1193  auth = _dbus_auth_new (sizeof (DBusAuthServer));
1194  if (auth == NULL)
1195    return NULL;
1196
1197  auth->handlers = server_handlers;
1198
1199  return auth;
1200}
1201
1202/**
1203 * Creates a new auth conversation object for the client side.
1204 * See doc/dbus-sasl-profile.txt for full details on what
1205 * this object does.
1206 *
1207 * @returns the new object or #NULL if no memory
1208 */
1209DBusAuth*
1210_dbus_auth_client_new (void)
1211{
1212  DBusAuth *auth;
1213
1214  auth = _dbus_auth_new (sizeof (DBusAuthClient));
1215  if (auth == NULL)
1216    return NULL;
1217
1218  auth->handlers = client_handlers;
1219
1220  /* Add a default mechanism to try */
1221  if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1222                          (void*) &all_mechanisms[0]))
1223    {
1224      _dbus_auth_unref (auth);
1225      return NULL;
1226    }
1227
1228  /* Now try the mechanism we just added */
1229  if (!client_try_next_mechanism (auth))
1230    {
1231      _dbus_auth_unref (auth);
1232      return NULL;
1233    }
1234
1235  return auth;
1236}
1237
1238/**
1239 * Increments the refcount of an auth object.
1240 *
1241 * @param auth the auth conversation
1242 */
1243void
1244_dbus_auth_ref (DBusAuth *auth)
1245{
1246  _dbus_assert (auth != NULL);
1247
1248  auth->refcount += 1;
1249}
1250
1251/**
1252 * Decrements the refcount of an auth object.
1253 *
1254 * @param auth the auth conversation
1255 */
1256void
1257_dbus_auth_unref (DBusAuth *auth)
1258{
1259  _dbus_assert (auth != NULL);
1260  _dbus_assert (auth->refcount > 0);
1261
1262  auth->refcount -= 1;
1263  if (auth->refcount == 0)
1264    {
1265      shutdown_mech (auth);
1266
1267      if (DBUS_AUTH_IS_CLIENT (auth))
1268        {
1269          _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1270        }
1271
1272      _dbus_string_free (&auth->identity);
1273      _dbus_string_free (&auth->incoming);
1274      _dbus_string_free (&auth->outgoing);
1275      dbus_free (auth);
1276    }
1277}
1278
1279/**
1280 * @param auth the auth conversation object
1281 * @returns #TRUE if we're in a final state
1282 */
1283#define DBUS_AUTH_IN_END_STATE(auth) ((auth)->need_disconnect || (auth)->authenticated)
1284
1285/**
1286 * Analyzes buffered input and moves the auth conversation forward,
1287 * returning the new state of the auth conversation.
1288 *
1289 * @param auth the auth conversation
1290 * @returns the new state
1291 */
1292DBusAuthState
1293_dbus_auth_do_work (DBusAuth *auth)
1294{
1295  if (DBUS_AUTH_IN_END_STATE (auth))
1296    return get_state (auth);
1297
1298  auth->needed_memory = FALSE;
1299
1300  /* Max amount we'll buffer up before deciding someone's on crack */
1301#define MAX_BUFFER (16 * 1024)
1302
1303  do
1304    {
1305      if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
1306          _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
1307        {
1308          auth->need_disconnect = TRUE;
1309          _dbus_verbose ("Disconnecting due to excessive data buffered in auth phase\n");
1310          break;
1311        }
1312
1313      if (auth->mech == NULL &&
1314          auth->already_got_mechanisms &&
1315          DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
1316        {
1317          auth->need_disconnect = TRUE;
1318          _dbus_verbose ("Disconnecting because we are out of mechanisms to try using\n");
1319          break;
1320        }
1321    }
1322  while (process_command (auth));
1323
1324  return get_state (auth);
1325}
1326
1327/**
1328 * Gets bytes that need to be sent to the peer we're conversing with.
1329 * After writing some bytes, _dbus_auth_bytes_sent() must be called
1330 * to notify the auth object that they were written.
1331 *
1332 * @param auth the auth conversation
1333 * @param str return location for a ref to the buffer to send
1334 * @returns #FALSE if nothing to send
1335 */
1336dbus_bool_t
1337_dbus_auth_get_bytes_to_send (DBusAuth          *auth,
1338                              const DBusString **str)
1339{
1340  _dbus_assert (auth != NULL);
1341  _dbus_assert (str != NULL);
1342
1343  *str = NULL;
1344
1345  if (DBUS_AUTH_IN_END_STATE (auth))
1346    return FALSE;
1347
1348  if (_dbus_string_get_length (&auth->outgoing) == 0)
1349    return FALSE;
1350
1351  *str = &auth->outgoing;
1352
1353  return TRUE;
1354}
1355
1356/**
1357 * Notifies the auth conversation object that
1358 * the given number of bytes of the outgoing buffer
1359 * have been written out.
1360 *
1361 * @param auth the auth conversation
1362 * @param bytes_sent number of bytes written out
1363 */
1364void
1365_dbus_auth_bytes_sent (DBusAuth *auth,
1366                       int       bytes_sent)
1367{
1368  _dbus_string_delete (&auth->outgoing,
1369                       0, bytes_sent);
1370
1371  if (auth->authenticated_pending_output &&
1372      _dbus_string_get_length (&auth->outgoing) == 0)
1373    auth->authenticated = TRUE;
1374}
1375
1376/**
1377 * Stores bytes received from the peer we're conversing with.
1378 *
1379 * @param auth the auth conversation
1380 * @param str the received bytes.
1381 * @returns #FALSE if not enough memory to store the bytes.
1382 */
1383dbus_bool_t
1384_dbus_auth_bytes_received (DBusAuth   *auth,
1385                           const DBusString *str)
1386{
1387  _dbus_assert (auth != NULL);
1388  _dbus_assert (str != NULL);
1389
1390  if (DBUS_AUTH_IN_END_STATE (auth))
1391    return FALSE;
1392
1393  auth->needed_memory = FALSE;
1394
1395  if (!_dbus_string_copy (str, 0,
1396                          &auth->incoming,
1397                          _dbus_string_get_length (&auth->incoming)))
1398    {
1399      auth->needed_memory = TRUE;
1400      return FALSE;
1401    }
1402
1403  _dbus_auth_do_work (auth);
1404
1405  return TRUE;
1406}
1407
1408/**
1409 * Returns leftover bytes that were not used as part of the auth
1410 * conversation.  These bytes will be part of the message stream
1411 * instead. This function may not be called until authentication has
1412 * succeeded.
1413 *
1414 * @param auth the auth conversation
1415 * @param str string to append the unused bytes to
1416 * @returns #FALSE if not enough memory to return the bytes
1417 */
1418dbus_bool_t
1419_dbus_auth_get_unused_bytes (DBusAuth   *auth,
1420                             DBusString *str)
1421{
1422  if (!DBUS_AUTH_IN_END_STATE (auth))
1423    return FALSE;
1424
1425  if (!_dbus_string_move (&auth->incoming,
1426                          0, str,
1427                          _dbus_string_get_length (str)))
1428    return FALSE;
1429
1430  return TRUE;
1431}
1432
1433/**
1434 * Called post-authentication, indicates whether we need to encode
1435 * the message stream with _dbus_auth_encode_data() prior to
1436 * sending it to the peer.
1437 *
1438 * @param auth the auth conversation
1439 * @returns #TRUE if we need to encode the stream
1440 */
1441dbus_bool_t
1442_dbus_auth_needs_encoding (DBusAuth *auth)
1443{
1444  if (!auth->authenticated)
1445    return FALSE;
1446
1447  if (auth->mech != NULL)
1448    {
1449      if (DBUS_AUTH_IS_CLIENT (auth))
1450        return auth->mech->client_encode_func != NULL;
1451      else
1452        return auth->mech->server_encode_func != NULL;
1453    }
1454  else
1455    return FALSE;
1456}
1457
1458/**
1459 * Called post-authentication, encodes a block of bytes for sending to
1460 * the peer. If no encoding was negotiated, just copies the bytes
1461 * (you can avoid this by checking _dbus_auth_needs_encoding()).
1462 *
1463 * @param auth the auth conversation
1464 * @param plaintext the plain text data
1465 * @param encoded initialized string to where encoded data is appended
1466 * @returns #TRUE if we had enough memory and successfully encoded
1467 */
1468dbus_bool_t
1469_dbus_auth_encode_data (DBusAuth         *auth,
1470                        const DBusString *plaintext,
1471                        DBusString       *encoded)
1472{
1473  _dbus_assert (plaintext != encoded);
1474
1475  if (!auth->authenticated)
1476    return FALSE;
1477
1478  if (_dbus_auth_needs_encoding (auth))
1479    {
1480      if (DBUS_AUTH_IS_CLIENT (auth))
1481        return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
1482      else
1483        return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
1484    }
1485  else
1486    {
1487      return _dbus_string_copy (plaintext, 0, encoded,
1488                                _dbus_string_get_length (encoded));
1489    }
1490}
1491
1492/**
1493 * Called post-authentication, indicates whether we need to decode
1494 * the message stream with _dbus_auth_decode_data() after
1495 * receiving it from the peer.
1496 *
1497 * @param auth the auth conversation
1498 * @returns #TRUE if we need to encode the stream
1499 */
1500dbus_bool_t
1501_dbus_auth_needs_decoding (DBusAuth *auth)
1502{
1503  if (!auth->authenticated)
1504    return FALSE;
1505
1506  if (auth->mech != NULL)
1507    {
1508      if (DBUS_AUTH_IS_CLIENT (auth))
1509        return auth->mech->client_decode_func != NULL;
1510      else
1511        return auth->mech->server_decode_func != NULL;
1512    }
1513  else
1514    return FALSE;
1515}
1516
1517
1518/**
1519 * Called post-authentication, decodes a block of bytes received from
1520 * the peer. If no encoding was negotiated, just copies the bytes (you
1521 * can avoid this by checking _dbus_auth_needs_decoding()).
1522 *
1523 * @todo We need to be able to distinguish "out of memory" error
1524 * from "the data is hosed" error.
1525 *
1526 * @param auth the auth conversation
1527 * @param encoded the encoded data
1528 * @param plaintext initialized string where decoded data is appended
1529 * @returns #TRUE if we had enough memory and successfully decoded
1530 */
1531dbus_bool_t
1532_dbus_auth_decode_data (DBusAuth         *auth,
1533                        const DBusString *encoded,
1534                        DBusString       *plaintext)
1535{
1536  _dbus_assert (plaintext != encoded);
1537
1538  if (!auth->authenticated)
1539    return FALSE;
1540
1541  if (_dbus_auth_needs_decoding (auth))
1542    {
1543      if (DBUS_AUTH_IS_CLIENT (auth))
1544        return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
1545      else
1546        return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
1547    }
1548  else
1549    {
1550      return _dbus_string_copy (encoded, 0, plaintext,
1551                                _dbus_string_get_length (plaintext));
1552    }
1553}
1554
1555/**
1556 * Sets credentials received via reliable means from the operating
1557 * system.
1558 *
1559 * @param auth the auth conversation
1560 * @param credentials the credentials received
1561 */
1562void
1563_dbus_auth_set_credentials (DBusAuth               *auth,
1564                            const DBusCredentials  *credentials)
1565{
1566  auth->credentials = *credentials;
1567}
1568
1569/**
1570 * Gets the identity we authorized the client as.  Apps may have
1571 * different policies as to what identities they allow.
1572 *
1573 * @param auth the auth conversation
1574 * @param credentials the credentials we've authorized
1575 */
1576void
1577_dbus_auth_get_identity (DBusAuth               *auth,
1578                         DBusCredentials        *credentials)
1579{
1580  if (auth->authenticated)
1581    {
1582      *credentials = auth->authorized_identity;
1583    }
1584  else
1585    {
1586      credentials->pid = -1;
1587      credentials->uid = -1;
1588      credentials->gid = -1;
1589    }
1590}
1591
1592
1593/** @} */
1594