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