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