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