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