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