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