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