2003-01-26 Anders Carlsson <andersca@codefactory.se>
[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       _dbus_string_free (&mech);      
719       _dbus_string_free (&base64_response);
720       _dbus_string_free (&decoded_response);
721
722       return TRUE;
723       
724     failed:
725       auth->mech = NULL;
726       _dbus_string_free (&mech);
727       _dbus_string_free (&base64_response);
728       _dbus_string_free (&decoded_response);
729       return FALSE;
730     }
731 }
732
733 static dbus_bool_t
734 process_cancel (DBusAuth         *auth,
735                 const DBusString *command,
736                 const DBusString *args)
737 {
738   shutdown_mech (auth);
739   
740   return TRUE;
741 }
742
743 static dbus_bool_t
744 process_begin (DBusAuth         *auth,
745                const DBusString *command,
746                const DBusString *args)
747 {
748   if (auth->authenticated_pending_begin)
749     auth->authenticated = TRUE;
750   else
751     {
752       auth->need_disconnect = TRUE; /* client trying to send data before auth,
753                                      * kick it
754                                      */
755       shutdown_mech (auth);
756     }
757   
758   return TRUE;
759 }
760
761 static dbus_bool_t
762 process_data_server (DBusAuth         *auth,
763                      const DBusString *command,
764                      const DBusString *args)
765 {
766   if (auth->mech != NULL)
767     {
768       DBusString decoded;
769
770       if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
771         return FALSE;
772
773       if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
774         {
775           _dbus_string_free (&decoded);
776           return FALSE;
777         }
778       
779       if (!(* auth->mech->server_data_func) (auth, &decoded))
780         {
781           _dbus_string_free (&decoded);
782           return FALSE;
783         }
784
785       _dbus_string_free (&decoded);
786     }
787   else
788     {
789       if (!_dbus_string_append (&auth->outgoing,
790                                 "ERROR \"Not currently in an auth conversation\"\r\n"))
791         return FALSE;
792     }
793   
794   return TRUE;
795 }
796
797 static dbus_bool_t
798 process_error_server (DBusAuth         *auth,
799                       const DBusString *command,
800                       const DBusString *args)
801 {
802   
803   return TRUE;
804 }
805
806 /* return FALSE if no memory, TRUE if all OK */
807 static dbus_bool_t
808 get_word (const DBusString *str,
809           int              *start,
810           DBusString       *word)
811 {
812   int i;
813
814   _dbus_string_skip_blank (str, *start, start);
815   _dbus_string_find_blank (str, *start, &i);
816   
817   if (i > *start)
818     {
819       if (!_dbus_string_copy_len (str, *start, i, word, 0))
820         return FALSE;
821       
822       *start = i;
823     }
824
825   return TRUE;
826 }
827
828 static dbus_bool_t
829 process_mechanisms (DBusAuth         *auth,
830                     const DBusString *command,
831                     const DBusString *args)
832 {
833   int next;
834   int len;
835
836   if (auth->already_got_mechanisms)
837     return TRUE;
838   
839   len = _dbus_string_get_length (args);
840   
841   next = 0;
842   while (next < len)
843     {
844       DBusString m;
845       const DBusAuthMechanismHandler *mech;
846       
847       if (!_dbus_string_init (&m, _DBUS_INT_MAX))
848         goto nomem;
849       
850       if (!get_word (args, &next, &m))
851         goto nomem;
852
853       mech = find_mech (&m);
854
855       if (mech != NULL)
856         {
857           /* FIXME right now we try mechanisms in the order
858            * the server lists them; should we do them in
859            * some more deterministic order?
860            *
861            * Probably in all_mechanisms order, our order of
862            * preference. Of course when the server is us,
863            * it lists things in that order anyhow.
864            */
865
866           _dbus_verbose ("Adding mechanism %s to list we will try\n",
867                          mech->mechanism);
868           
869           if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
870                                   (void*) mech))
871             goto nomem;
872         }
873       else
874         {
875           const char *s;
876
877           _dbus_string_get_const_data (&m, &s);
878           _dbus_verbose ("Server offered mechanism \"%s\" that we don't know how to use\n",
879                          s);
880         }
881
882       _dbus_string_free (&m);
883     }
884   
885   auth->already_got_mechanisms = TRUE;
886   
887   return TRUE;
888
889  nomem:
890   _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
891   
892   return FALSE;
893 }
894
895 static dbus_bool_t
896 client_try_next_mechanism (DBusAuth *auth)
897 {
898   const DBusAuthMechanismHandler *mech;
899   DBusString auth_command;
900
901   if (DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
902     return FALSE;
903
904   mech = DBUS_AUTH_CLIENT (auth)->mechs_to_try->data;
905
906   if (!_dbus_string_init (&auth_command, _DBUS_INT_MAX))
907     return FALSE;
908       
909   if (!_dbus_string_append (&auth_command,
910                             "AUTH "))
911     {
912       _dbus_string_free (&auth_command);
913       return FALSE;
914     }  
915   
916   if (!_dbus_string_append (&auth_command,
917                             mech->mechanism))
918     {
919       _dbus_string_free (&auth_command);
920       return FALSE;
921     }
922
923   if (mech->client_initial_response_func != NULL)
924     {
925       if (!_dbus_string_append (&auth_command, " "))
926         {
927           _dbus_string_free (&auth_command);
928           return FALSE;
929         }
930       
931       if (!(* mech->client_initial_response_func) (auth, &auth_command))
932         {
933           _dbus_string_free (&auth_command);
934           return FALSE;
935         }
936     }
937   
938   if (!_dbus_string_append (&auth_command,
939                             "\r\n"))
940     {
941       _dbus_string_free (&auth_command);
942       return FALSE;
943     }
944
945   if (!_dbus_string_copy (&auth_command, 0,
946                           &auth->outgoing,
947                           _dbus_string_get_length (&auth->outgoing)))
948     {
949       _dbus_string_free (&auth_command);
950       return FALSE;
951     }
952
953   auth->mech = mech;      
954   _dbus_list_pop_first (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
955
956   _dbus_verbose ("Trying mechanism %s\n",
957                  auth->mech->mechanism);
958
959   return TRUE;
960 }
961
962 static dbus_bool_t
963 process_rejected (DBusAuth         *auth,
964                   const DBusString *command,
965                   const DBusString *args)
966 {
967   shutdown_mech (auth);
968   
969   if (!auth->already_got_mechanisms)
970     {
971       /* Ask for mechanisms */
972       if (!_dbus_string_append (&auth->outgoing,
973                                 "AUTH\r\n"))
974         return FALSE;
975     }
976   else if (DBUS_AUTH_CLIENT (auth)->mechs_to_try != NULL)
977     {
978       client_try_next_mechanism (auth);
979     }
980   else
981     {
982       /* Give up */
983       auth->need_disconnect = TRUE;
984     }
985   
986   return TRUE;
987 }
988
989 static dbus_bool_t
990 process_ok (DBusAuth         *auth,
991             const DBusString *command,
992             const DBusString *args)
993 {
994   if (!_dbus_string_append (&auth->outgoing,
995                             "BEGIN\r\n"))
996     return FALSE;
997   
998   auth->authenticated_pending_output = TRUE;
999   
1000   return TRUE;
1001 }
1002
1003
1004 static dbus_bool_t
1005 process_data_client (DBusAuth         *auth,
1006                      const DBusString *command,
1007                      const DBusString *args)
1008 {
1009   if (auth->mech != NULL)
1010     {
1011       DBusString decoded;
1012
1013       if (!_dbus_string_init (&decoded, _DBUS_INT_MAX))
1014         return FALSE;
1015
1016       if (!_dbus_string_base64_decode (args, 0, &decoded, 0))
1017         {
1018           _dbus_string_free (&decoded);
1019           return FALSE;
1020         }
1021       
1022       if (!(* auth->mech->client_data_func) (auth, &decoded))
1023         {
1024           _dbus_string_free (&decoded);
1025           return FALSE;
1026         }
1027
1028       _dbus_string_free (&decoded);
1029     }
1030   else
1031     {
1032       if (!_dbus_string_append (&auth->outgoing,
1033                                 "ERROR \"Got DATA when not in an auth exchange\"\r\n"))
1034         return FALSE;
1035     }
1036   
1037   return TRUE;
1038 }
1039
1040 static dbus_bool_t
1041 process_error_client (DBusAuth         *auth,
1042                       const DBusString *command,
1043                       const DBusString *args)
1044 {
1045   return TRUE;
1046 }
1047
1048 static dbus_bool_t
1049 process_unknown (DBusAuth         *auth,
1050                  const DBusString *command,
1051                  const DBusString *args)
1052 {
1053   if (!_dbus_string_append (&auth->outgoing,
1054                             "ERROR \"Unknown command\"\r\n"))
1055     return FALSE;
1056
1057   return TRUE;
1058 }
1059
1060 /* returns whether to call it again right away */
1061 static dbus_bool_t
1062 process_command (DBusAuth *auth)
1063 {
1064   DBusString command;
1065   DBusString args;
1066   int eol;
1067   int i, j;
1068   dbus_bool_t retval;
1069
1070   retval = FALSE;
1071   
1072   eol = 0;
1073   if (!_dbus_string_find (&auth->incoming, 0, "\r\n", &eol))
1074     return FALSE;
1075   
1076   if (!_dbus_string_init (&command, _DBUS_INT_MAX))
1077     {
1078       auth->needed_memory = TRUE;
1079       return FALSE;
1080     }
1081
1082   if (!_dbus_string_init (&args, _DBUS_INT_MAX))
1083     {
1084       auth->needed_memory = TRUE;
1085       return FALSE;
1086     }
1087   
1088   if (eol > _DBUS_ONE_MEGABYTE)
1089     {
1090       /* This is a giant line, someone is trying to hose us. */
1091       if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command too long\"\r\n"))
1092         goto out;
1093       else
1094         goto next_command;
1095     }
1096
1097   if (!_dbus_string_copy_len (&auth->incoming, 0, eol, &command, 0))
1098     goto out;
1099
1100   if (!_dbus_string_validate_ascii (&command, 0,
1101                                     _dbus_string_get_length (&command)))
1102     {
1103       _dbus_verbose ("Command contained non-ASCII chars or embedded nul\n");
1104       if (!_dbus_string_append (&auth->outgoing, "ERROR \"Command contained non-ASCII\"\r\n"))
1105         goto out;
1106       else
1107         goto next_command;
1108     }
1109   
1110   {
1111     const char *q;
1112     _dbus_string_get_const_data (&command, &q);
1113     _dbus_verbose ("got command \"%s\"\n", q);
1114   }
1115   
1116   _dbus_string_find_blank (&command, 0, &i);
1117   _dbus_string_skip_blank (&command, i, &j);
1118
1119   if (j > i)
1120     _dbus_string_delete (&command, i, j - i);
1121   
1122   if (!_dbus_string_move (&command, i, &args, 0))
1123     goto out;
1124   
1125   i = 0;
1126   while (auth->handlers[i].command != NULL)
1127     {
1128       if (_dbus_string_equal_c_str (&command,
1129                                     auth->handlers[i].command))
1130         {
1131           _dbus_verbose ("Processing auth command %s\n",
1132                          auth->handlers[i].command);
1133           
1134           if (!(* auth->handlers[i].func) (auth, &command, &args))
1135             goto out;
1136
1137           break;
1138         }
1139       ++i;
1140     }
1141
1142   if (auth->handlers[i].command == NULL)
1143     {
1144       if (!process_unknown (auth, &command, &args))
1145         goto out;
1146     }
1147
1148  next_command:
1149   
1150   /* We've succeeded in processing the whole command so drop it out
1151    * of the incoming buffer and return TRUE to try another command.
1152    */
1153
1154   _dbus_string_delete (&auth->incoming, 0, eol);
1155   
1156   /* kill the \r\n */
1157   _dbus_string_delete (&auth->incoming, 0, 2);
1158
1159   retval = TRUE;
1160   
1161  out:
1162   _dbus_string_free (&args);
1163   _dbus_string_free (&command);
1164
1165   if (!retval)
1166     auth->needed_memory = TRUE;
1167   else
1168     auth->needed_memory = FALSE;
1169   
1170   return retval;
1171 }
1172
1173
1174 /** @} */
1175
1176 /**
1177  * @addtogroup DBusAuth
1178  * @{
1179  */
1180
1181 /**
1182  * Creates a new auth conversation object for the server side.
1183  * See doc/dbus-sasl-profile.txt for full details on what
1184  * this object does.
1185  *
1186  * @returns the new object or #NULL if no memory
1187  */
1188 DBusAuth*
1189 _dbus_auth_server_new (void)
1190 {
1191   DBusAuth *auth;
1192
1193   auth = _dbus_auth_new (sizeof (DBusAuthServer));
1194   if (auth == NULL)
1195     return NULL;
1196
1197   auth->handlers = server_handlers;
1198   
1199   return auth;
1200 }
1201
1202 /**
1203  * Creates a new auth conversation object for the client side.
1204  * See doc/dbus-sasl-profile.txt for full details on what
1205  * this object does.
1206  *
1207  * @returns the new object or #NULL if no memory
1208  */
1209 DBusAuth*
1210 _dbus_auth_client_new (void)
1211 {
1212   DBusAuth *auth;
1213
1214   auth = _dbus_auth_new (sizeof (DBusAuthClient));
1215   if (auth == NULL)
1216     return NULL;
1217
1218   auth->handlers = client_handlers;
1219
1220   /* Add a default mechanism to try */
1221   if (!_dbus_list_append (& DBUS_AUTH_CLIENT (auth)->mechs_to_try,
1222                           (void*) &all_mechanisms[0]))
1223     {
1224       _dbus_auth_unref (auth);
1225       return NULL;
1226     }
1227
1228   /* Now try the mechanism we just added */
1229   if (!client_try_next_mechanism (auth))
1230     {
1231       _dbus_auth_unref (auth);
1232       return NULL;
1233     }
1234   
1235   return auth;
1236 }
1237
1238 /**
1239  * Increments the refcount of an auth object.
1240  *
1241  * @param auth the auth conversation
1242  */
1243 void
1244 _dbus_auth_ref (DBusAuth *auth)
1245 {
1246   _dbus_assert (auth != NULL);
1247   
1248   auth->refcount += 1;
1249 }
1250
1251 /**
1252  * Decrements the refcount of an auth object.
1253  *
1254  * @param auth the auth conversation
1255  */
1256 void
1257 _dbus_auth_unref (DBusAuth *auth)
1258 {
1259   _dbus_assert (auth != NULL);
1260   _dbus_assert (auth->refcount > 0);
1261
1262   auth->refcount -= 1;
1263   if (auth->refcount == 0)
1264     {
1265       shutdown_mech (auth);
1266
1267       if (DBUS_AUTH_IS_CLIENT (auth))
1268         {
1269           _dbus_list_clear (& DBUS_AUTH_CLIENT (auth)->mechs_to_try);
1270         }
1271
1272       _dbus_string_free (&auth->identity);
1273       _dbus_string_free (&auth->incoming);
1274       _dbus_string_free (&auth->outgoing);
1275       dbus_free (auth);
1276     }
1277 }
1278
1279 /**
1280  * @param auth the auth conversation object
1281  * @returns #TRUE if we're in a final state
1282  */
1283 #define DBUS_AUTH_IN_END_STATE(auth) ((auth)->need_disconnect || (auth)->authenticated)
1284
1285 /**
1286  * Analyzes buffered input and moves the auth conversation forward,
1287  * returning the new state of the auth conversation.
1288  *
1289  * @param auth the auth conversation
1290  * @returns the new state
1291  */
1292 DBusAuthState
1293 _dbus_auth_do_work (DBusAuth *auth)
1294 {  
1295   if (DBUS_AUTH_IN_END_STATE (auth))
1296     return get_state (auth);
1297
1298   auth->needed_memory = FALSE;
1299
1300   /* Max amount we'll buffer up before deciding someone's on crack */
1301 #define MAX_BUFFER (16 * 1024)
1302
1303   do
1304     {
1305       if (_dbus_string_get_length (&auth->incoming) > MAX_BUFFER ||
1306           _dbus_string_get_length (&auth->outgoing) > MAX_BUFFER)
1307         {
1308           auth->need_disconnect = TRUE;
1309           _dbus_verbose ("Disconnecting due to excessive data buffered in auth phase\n");
1310           break;
1311         }
1312
1313       if (auth->mech == NULL &&
1314           auth->already_got_mechanisms &&
1315           DBUS_AUTH_CLIENT (auth)->mechs_to_try == NULL)
1316         {
1317           auth->need_disconnect = TRUE;
1318           _dbus_verbose ("Disconnecting because we are out of mechanisms to try using\n");
1319           break;
1320         }
1321     }
1322   while (process_command (auth));
1323   
1324   return get_state (auth);
1325 }
1326
1327 /**
1328  * Gets bytes that need to be sent to the peer we're conversing with.
1329  * After writing some bytes, _dbus_auth_bytes_sent() must be called
1330  * to notify the auth object that they were written.
1331  *
1332  * @param auth the auth conversation
1333  * @param str return location for a ref to the buffer to send
1334  * @returns #FALSE if nothing to send
1335  */
1336 dbus_bool_t
1337 _dbus_auth_get_bytes_to_send (DBusAuth          *auth,
1338                               const DBusString **str)
1339 {
1340   _dbus_assert (auth != NULL);
1341   _dbus_assert (str != NULL);
1342
1343   *str = NULL;
1344   
1345   if (DBUS_AUTH_IN_END_STATE (auth))
1346     return FALSE;
1347
1348   if (_dbus_string_get_length (&auth->outgoing) == 0)
1349     return FALSE;
1350
1351   *str = &auth->outgoing;
1352
1353   return TRUE;
1354 }
1355
1356 /**
1357  * Notifies the auth conversation object that
1358  * the given number of bytes of the outgoing buffer
1359  * have been written out.
1360  *
1361  * @param auth the auth conversation
1362  * @param bytes_sent number of bytes written out
1363  */
1364 void
1365 _dbus_auth_bytes_sent (DBusAuth *auth,
1366                        int       bytes_sent)
1367 {
1368   _dbus_string_delete (&auth->outgoing,
1369                        0, bytes_sent);
1370   
1371   if (auth->authenticated_pending_output &&
1372       _dbus_string_get_length (&auth->outgoing) == 0)
1373     auth->authenticated = TRUE;
1374 }
1375
1376 /**
1377  * Stores bytes received from the peer we're conversing with.
1378  *
1379  * @param auth the auth conversation
1380  * @param str the received bytes.
1381  * @returns #FALSE if not enough memory to store the bytes.
1382  */
1383 dbus_bool_t
1384 _dbus_auth_bytes_received (DBusAuth   *auth,
1385                            const DBusString *str)
1386 {
1387   _dbus_assert (auth != NULL);
1388   _dbus_assert (str != NULL);
1389   
1390   if (DBUS_AUTH_IN_END_STATE (auth))
1391     return FALSE;
1392
1393   auth->needed_memory = FALSE;
1394   
1395   if (!_dbus_string_copy (str, 0,
1396                           &auth->incoming,
1397                           _dbus_string_get_length (&auth->incoming)))
1398     {
1399       auth->needed_memory = TRUE;
1400       return FALSE;
1401     }
1402
1403   _dbus_auth_do_work (auth);
1404   
1405   return TRUE;
1406 }
1407
1408 /**
1409  * Returns leftover bytes that were not used as part of the auth
1410  * conversation.  These bytes will be part of the message stream
1411  * instead. This function may not be called until authentication has
1412  * succeeded.
1413  *
1414  * @param auth the auth conversation
1415  * @param str string to append the unused bytes to
1416  * @returns #FALSE if not enough memory to return the bytes
1417  */
1418 dbus_bool_t
1419 _dbus_auth_get_unused_bytes (DBusAuth   *auth,
1420                              DBusString *str)
1421 {
1422   if (!DBUS_AUTH_IN_END_STATE (auth))
1423     return FALSE;
1424   
1425   if (!_dbus_string_move (&auth->incoming,
1426                           0, str,
1427                           _dbus_string_get_length (str)))
1428     return FALSE;
1429
1430   return TRUE;
1431 }
1432
1433 /**
1434  * Called post-authentication, indicates whether we need to encode
1435  * the message stream with _dbus_auth_encode_data() prior to
1436  * sending it to the peer.
1437  *
1438  * @param auth the auth conversation
1439  * @returns #TRUE if we need to encode the stream
1440  */
1441 dbus_bool_t
1442 _dbus_auth_needs_encoding (DBusAuth *auth)
1443 {
1444   if (!auth->authenticated)
1445     return FALSE;
1446   
1447   if (auth->mech != NULL)
1448     {
1449       if (DBUS_AUTH_IS_CLIENT (auth))
1450         return auth->mech->client_encode_func != NULL;
1451       else
1452         return auth->mech->server_encode_func != NULL;
1453     }
1454   else
1455     return FALSE;
1456 }
1457
1458 /**
1459  * Called post-authentication, encodes a block of bytes for sending to
1460  * the peer. If no encoding was negotiated, just copies the bytes
1461  * (you can avoid this by checking _dbus_auth_needs_encoding()).
1462  *
1463  * @param auth the auth conversation
1464  * @param plaintext the plain text data
1465  * @param encoded initialized string to where encoded data is appended
1466  * @returns #TRUE if we had enough memory and successfully encoded
1467  */
1468 dbus_bool_t
1469 _dbus_auth_encode_data (DBusAuth         *auth,
1470                         const DBusString *plaintext,
1471                         DBusString       *encoded)
1472 {
1473   _dbus_assert (plaintext != encoded);
1474   
1475   if (!auth->authenticated)
1476     return FALSE;
1477   
1478   if (_dbus_auth_needs_encoding (auth))
1479     {
1480       if (DBUS_AUTH_IS_CLIENT (auth))
1481         return (* auth->mech->client_encode_func) (auth, plaintext, encoded);
1482       else
1483         return (* auth->mech->server_encode_func) (auth, plaintext, encoded);
1484     }
1485   else
1486     {
1487       return _dbus_string_copy (plaintext, 0, encoded,
1488                                 _dbus_string_get_length (encoded));
1489     }
1490 }
1491
1492 /**
1493  * Called post-authentication, indicates whether we need to decode
1494  * the message stream with _dbus_auth_decode_data() after
1495  * receiving it from the peer.
1496  *
1497  * @param auth the auth conversation
1498  * @returns #TRUE if we need to encode the stream
1499  */
1500 dbus_bool_t
1501 _dbus_auth_needs_decoding (DBusAuth *auth)
1502 {
1503   if (!auth->authenticated)
1504     return FALSE;
1505     
1506   if (auth->mech != NULL)
1507     {
1508       if (DBUS_AUTH_IS_CLIENT (auth))
1509         return auth->mech->client_decode_func != NULL;
1510       else
1511         return auth->mech->server_decode_func != NULL;
1512     }
1513   else
1514     return FALSE;
1515 }
1516
1517
1518 /**
1519  * Called post-authentication, decodes a block of bytes received from
1520  * the peer. If no encoding was negotiated, just copies the bytes (you
1521  * can avoid this by checking _dbus_auth_needs_decoding()).
1522  *
1523  * @todo We need to be able to distinguish "out of memory" error
1524  * from "the data is hosed" error.
1525  *
1526  * @param auth the auth conversation
1527  * @param encoded the encoded data
1528  * @param plaintext initialized string where decoded data is appended
1529  * @returns #TRUE if we had enough memory and successfully decoded
1530  */
1531 dbus_bool_t
1532 _dbus_auth_decode_data (DBusAuth         *auth,
1533                         const DBusString *encoded,
1534                         DBusString       *plaintext)
1535 {
1536   _dbus_assert (plaintext != encoded);
1537   
1538   if (!auth->authenticated)
1539     return FALSE;
1540   
1541   if (_dbus_auth_needs_decoding (auth))
1542     {
1543       if (DBUS_AUTH_IS_CLIENT (auth))
1544         return (* auth->mech->client_decode_func) (auth, encoded, plaintext);
1545       else
1546         return (* auth->mech->server_decode_func) (auth, encoded, plaintext);
1547     }
1548   else
1549     {
1550       return _dbus_string_copy (encoded, 0, plaintext,
1551                                 _dbus_string_get_length (plaintext));
1552     }
1553 }
1554
1555 /**
1556  * Sets credentials received via reliable means from the operating
1557  * system.
1558  *
1559  * @param auth the auth conversation
1560  * @param credentials the credentials received
1561  */
1562 void
1563 _dbus_auth_set_credentials (DBusAuth               *auth,
1564                             const DBusCredentials  *credentials)
1565 {
1566   auth->credentials = *credentials;
1567 }
1568
1569 /**
1570  * Gets the identity we authorized the client as.  Apps may have
1571  * different policies as to what identities they allow.
1572  *
1573  * @param auth the auth conversation
1574  * @param credentials the credentials we've authorized
1575  */
1576 void
1577 _dbus_auth_get_identity (DBusAuth               *auth,
1578                          DBusCredentials        *credentials)
1579 {
1580   if (auth->authenticated)
1581     {
1582       *credentials = auth->authorized_identity;
1583     }
1584   else
1585     {
1586       credentials->pid = -1;
1587       credentials->uid = -1;
1588       credentials->gid = -1;
1589     }
1590 }
1591
1592
1593 /** @} */