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