GSS_S_PROMPTING_NEEDED is a bit
[cyrus-sasl.git] / plugins / plain.c
1 /* Plain SASL plugin
2  * Rob Siemborski
3  * Tim Martin 
4  * $Id: plain.c,v 1.64 2004/09/08 11:06:11 mel Exp $
5  */
6 /* 
7  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer. 
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  *
21  * 3. The name "Carnegie Mellon University" must not be used to
22  *    endorse or promote products derived from this software without
23  *    prior written permission. For permission or any other legal
24  *    details, please contact  
25  *      Office of Technology Transfer
26  *      Carnegie Mellon University
27  *      5000 Forbes Avenue
28  *      Pittsburgh, PA  15213-3890
29  *      (412) 268-4387, fax: (412) 268-7395
30  *      tech-transfer@andrew.cmu.edu
31  *
32  * 4. Redistributions of any form whatsoever must retain the following
33  *    acknowledgment:
34  *    "This product includes software developed by Computing Services
35  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
36  *
37  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
38  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
39  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
40  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
41  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
42  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
43  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
44  */
45
46 #include <config.h>
47 #include <stdio.h>
48 #include <string.h> 
49 #include <sasl.h>
50 #include <saslplug.h>
51
52 #include "plugin_common.h"
53
54 #ifdef macintosh 
55 #include <sasl_plain_plugin_decl.h> 
56 #endif 
57
58 /*****************************  Common Section  *****************************/
59
60 static const char plugin_id[] = "$Id: plain.c,v 1.64 2004/09/08 11:06:11 mel Exp $";
61
62 /*****************************  Server Section  *****************************/
63
64 static int plain_server_mech_new(void *glob_context __attribute__((unused)), 
65                                  sasl_server_params_t *sparams,
66                                  const char *challenge __attribute__((unused)),
67                                  unsigned challen __attribute__((unused)),
68                                  void **conn_context)
69 {
70     /* holds state are in */
71     if (!conn_context) {
72         PARAMERROR( sparams->utils );
73         return SASL_BADPARAM;
74     }
75     
76     *conn_context = NULL;
77     
78     return SASL_OK;
79 }
80
81 static int plain_server_mech_step(void *conn_context __attribute__((unused)),
82                                   sasl_server_params_t *params,
83                                   const char *clientin,
84                                   unsigned clientinlen,
85                                   const char **serverout,
86                                   unsigned *serveroutlen,
87                                   sasl_out_params_t *oparams)
88 {
89     const char *author;
90     const char *authen;
91     const char *password;
92     unsigned password_len;
93     unsigned lup=0;
94     int result;
95     char *passcopy; 
96     
97     *serverout = NULL;
98     *serveroutlen = 0;
99     
100     /* should have received author-id NUL authen-id NUL password */
101     
102     /* get author */
103     author = clientin;
104     while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
105     
106     if (lup >= clientinlen) {
107         SETERROR(params->utils, "Can only find author (no password)");
108         return SASL_BADPROT;
109     }
110     
111     /* get authen */
112     ++lup;
113     authen = clientin + lup;
114     while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
115     
116     if (lup >= clientinlen) {
117         params->utils->seterror(params->utils->conn, 0,
118                                 "Can only find author/en (no password)");
119         return SASL_BADPROT;
120     }
121     
122     /* get password */
123     lup++;
124     password = clientin + lup;
125     while ((lup < clientinlen) && (clientin[lup] != 0)) ++lup;
126     
127     password_len = (unsigned) (clientin + lup - password);
128     
129     if (lup != clientinlen) {
130         SETERROR(params->utils,
131                  "Got more data than we were expecting in the PLAIN plugin\n");
132         return SASL_BADPROT;
133     }
134     
135     /* this kinda sucks. we need password to be null terminated
136        but we can't assume there is an allocated byte at the end
137        of password so we have to copy it */
138     passcopy = params->utils->malloc(password_len + 1);    
139     if (passcopy == NULL) {
140         MEMERROR(params->utils);
141         return SASL_NOMEM;
142     }
143     
144     strncpy(passcopy, password, password_len);
145     passcopy[password_len] = '\0';
146    
147     /* Canonicalize userid first, so that password verification is only
148      * against the canonical id */
149     if (!author || !*author)
150         author = authen;
151     
152     result = params->canon_user(params->utils->conn,
153                                 authen, 0, SASL_CU_AUTHID, oparams);
154     if (result != SASL_OK) {
155         _plug_free_string(params->utils, &passcopy);
156         return result;
157     }
158     
159     /* verify password - return sasl_ok on success*/
160     result = params->utils->checkpass(params->utils->conn,
161                                       oparams->authid, oparams->alen,
162                                       passcopy, password_len);
163     
164     _plug_free_string(params->utils, &passcopy);
165     
166     if (result != SASL_OK) {
167         params->utils->seterror(params->utils->conn, 0,
168                                 "Password verification failed");
169         return result;
170     }
171
172     /* Canonicalize and store the authorization ID */
173     /* We need to do this after calling verify_user just in case verify_user
174      * needed to get auxprops itself */
175     result = params->canon_user(params->utils->conn,
176                                 author, 0, SASL_CU_AUTHZID, oparams);
177     if (result != SASL_OK) return result;
178
179     /* set oparams */
180     oparams->doneflag = 1;
181     oparams->mech_ssf = 0;
182     oparams->maxoutbuf = 0;
183     oparams->encode_context = NULL;
184     oparams->encode = NULL;
185     oparams->decode_context = NULL;
186     oparams->decode = NULL;
187     oparams->param_version = 0;
188     
189     return SASL_OK;
190 }
191
192 static sasl_server_plug_t plain_server_plugins[] = 
193 {
194     {
195         "PLAIN",                        /* mech_name */
196         0,                              /* max_ssf */
197         SASL_SEC_NOANONYMOUS,           /* security_flags */
198         SASL_FEAT_WANT_CLIENT_FIRST
199         | SASL_FEAT_ALLOWS_PROXY,       /* features */
200         NULL,                           /* glob_context */
201         &plain_server_mech_new,         /* mech_new */
202         &plain_server_mech_step,        /* mech_step */
203         NULL,                           /* mech_dispose */
204         NULL,                           /* mech_free */
205         NULL,                           /* setpass */
206         NULL,                           /* user_query */
207         NULL,                           /* idle */
208         NULL,                           /* mech_avail */
209         NULL                            /* spare */
210     }
211 };
212
213 int plain_server_plug_init(const sasl_utils_t *utils,
214                            int maxversion,
215                            int *out_version,
216                            sasl_server_plug_t **pluglist,
217                            int *plugcount)
218 {
219     if (maxversion < SASL_SERVER_PLUG_VERSION) {
220         SETERROR(utils, "PLAIN version mismatch");
221         return SASL_BADVERS;
222     }
223     
224     *out_version = SASL_SERVER_PLUG_VERSION;
225     *pluglist = plain_server_plugins;
226     *plugcount = 1;  
227     
228     return SASL_OK;
229 }
230
231 /*****************************  Client Section  *****************************/
232
233 typedef struct client_context {
234     char *out_buf;
235     unsigned out_buf_len;
236 } client_context_t;
237
238 static int plain_client_mech_new(void *glob_context __attribute__((unused)),
239                                  sasl_client_params_t *params,
240                                  void **conn_context)
241 {
242     client_context_t *text;
243     
244     /* holds state are in */
245     text = params->utils->malloc(sizeof(client_context_t));
246     if (text == NULL) {
247         MEMERROR( params->utils );
248         return SASL_NOMEM;
249     }
250     
251     memset(text, 0, sizeof(client_context_t));
252     
253     *conn_context = text;
254     
255     return SASL_OK;
256 }
257
258 static int plain_client_mech_step(void *conn_context,
259                                   sasl_client_params_t *params,
260                                   const char *serverin __attribute__((unused)),
261                                   unsigned serverinlen __attribute__((unused)),
262                                   sasl_interact_t **prompt_need,
263                                   const char **clientout,
264                                   unsigned *clientoutlen,
265                                   sasl_out_params_t *oparams)
266 {
267     client_context_t *text = (client_context_t *) conn_context;
268     const char *user = NULL, *authid = NULL;
269     sasl_secret_t *password = NULL;
270     unsigned int free_password = 0; /* set if we need to free password */
271     int user_result = SASL_OK;
272     int auth_result = SASL_OK;
273     int pass_result = SASL_OK;
274     int result;
275     char *p;
276     
277     *clientout = NULL;
278     *clientoutlen = 0;
279     
280     /* doesn't really matter how the server responds */
281     
282     /* check if sec layer strong enough */
283     if (params->props.min_ssf > params->external_ssf) {
284         SETERROR( params->utils, "SSF requested of PLAIN plugin");
285         return SASL_TOOWEAK;
286     }
287     
288     /* try to get the authid */    
289     if (oparams->authid == NULL) {
290         auth_result = _plug_get_authid(params->utils, &authid, prompt_need);
291         
292         if ((auth_result != SASL_OK) && (auth_result != SASL_INTERACT))
293             return auth_result;
294     }           
295     
296     /* try to get the userid */
297     if (oparams->user == NULL) {
298         user_result = _plug_get_userid(params->utils, &user, prompt_need);
299         
300         if ((user_result != SASL_OK) && (user_result != SASL_INTERACT))
301             return user_result;
302     }
303     
304     /* try to get the password */
305     if (password == NULL) {
306         pass_result = _plug_get_password(params->utils, &password,
307                                          &free_password, prompt_need);
308         
309         if ((pass_result != SASL_OK) && (pass_result != SASL_INTERACT))
310             return pass_result;
311     }
312     
313     /* free prompts we got */
314     if (prompt_need && *prompt_need) {
315         params->utils->free(*prompt_need);
316         *prompt_need = NULL;
317     }
318     
319     /* if there are prompts not filled in */
320     if ((user_result == SASL_INTERACT) || (auth_result == SASL_INTERACT) ||
321         (pass_result == SASL_INTERACT)) {
322         /* make the prompt list */
323         result =
324             _plug_make_prompts(params->utils, prompt_need,
325                                user_result == SASL_INTERACT ?
326                                "Please enter your authorization name" : NULL,
327                                NULL,
328                                auth_result == SASL_INTERACT ?
329                                "Please enter your authentication name" : NULL,
330                                NULL,
331                                pass_result == SASL_INTERACT ?
332                                "Please enter your password" : NULL, NULL,
333                                NULL, NULL, NULL,
334                                NULL, NULL, NULL);
335         if (result != SASL_OK) goto cleanup;
336         
337         return SASL_INTERACT;
338     }
339     
340     if (!password) {
341         PARAMERROR(params->utils);
342         return SASL_BADPARAM;
343     }
344
345     if (!user || !*user) {
346         result = params->canon_user(params->utils->conn, authid, 0,
347                                     SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
348     }
349     else {
350         result = params->canon_user(params->utils->conn, user, 0,
351                                     SASL_CU_AUTHZID, oparams);
352         if (result != SASL_OK) goto cleanup;
353         
354         result = params->canon_user(params->utils->conn, authid, 0,
355                                     SASL_CU_AUTHID, oparams);
356     }
357     if (result != SASL_OK) goto cleanup;
358     
359     /* send authorized id NUL authentication id NUL password */
360     *clientoutlen = ((user && *user ? oparams->ulen : 0) +
361                      1 + oparams->alen +
362                      1 + password->len);
363     
364     /* remember the extra NUL on the end for stupid clients */
365     result = _plug_buf_alloc(params->utils, &(text->out_buf),
366                              &(text->out_buf_len), *clientoutlen + 1);
367     if (result != SASL_OK) goto cleanup;
368     
369     memset(text->out_buf, 0, *clientoutlen + 1);
370     p = text->out_buf;
371     if (user && *user) {
372         memcpy(p, oparams->user, oparams->ulen);
373         p += oparams->ulen;
374     }
375     memcpy(++p, oparams->authid, oparams->alen);
376     p += oparams->alen;
377     memcpy(++p, password->data, password->len);
378     
379     *clientout = text->out_buf;
380     
381     /* set oparams */
382     oparams->doneflag = 1;
383     oparams->mech_ssf = 0;
384     oparams->maxoutbuf = 0;
385     oparams->encode_context = NULL;
386     oparams->encode = NULL;
387     oparams->decode_context = NULL;
388     oparams->decode = NULL;
389     oparams->param_version = 0;
390     
391     result = SASL_OK;
392
393   cleanup:
394     /* free sensitive info */
395     if (free_password) _plug_free_secret(params->utils, &password);
396     
397     return result;
398 }
399
400 static void plain_client_mech_dispose(void *conn_context,
401                                       const sasl_utils_t *utils)
402 {
403     client_context_t *text = (client_context_t *) conn_context;
404     
405     if (!text) return;
406     
407     if (text->out_buf) utils->free(text->out_buf);
408     
409     utils->free(text);
410 }
411
412 static sasl_client_plug_t plain_client_plugins[] = 
413 {
414     {
415         "PLAIN",                        /* mech_name */
416         0,                              /* max_ssf */
417         SASL_SEC_NOANONYMOUS,           /* security_flags */
418         SASL_FEAT_WANT_CLIENT_FIRST
419         | SASL_FEAT_ALLOWS_PROXY,       /* features */
420         NULL,                           /* required_prompts */
421         NULL,                           /* glob_context */
422         &plain_client_mech_new,         /* mech_new */
423         &plain_client_mech_step,        /* mech_step */
424         &plain_client_mech_dispose,     /* mech_dispose */
425         NULL,                           /* mech_free */
426         NULL,                           /* idle */
427         NULL,                           /* spare */
428         NULL                            /* spare */
429     }
430 };
431
432 int plain_client_plug_init(sasl_utils_t *utils,
433                            int maxversion,
434                            int *out_version,
435                            sasl_client_plug_t **pluglist,
436                            int *plugcount)
437 {
438     if (maxversion < SASL_CLIENT_PLUG_VERSION) {
439         SETERROR(utils, "PLAIN version mismatch");
440         return SASL_BADVERS;
441     }
442     
443     *out_version = SASL_CLIENT_PLUG_VERSION;
444     *pluglist = plain_client_plugins;
445     *plugcount = 1;
446     
447     return SASL_OK;
448 }