1/* Generic SASL plugin utility functions
2 * Rob Siemborski
3 * $Id: plugin_common.c,v 1.3 2006/02/03 22:33:14 snsimon Exp $
4 */
5/*
6 * Copyright (c) 2001 Carnegie Mellon University.  All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in
17 *    the documentation and/or other materials provided with the
18 *    distribution.
19 *
20 * 3. The name "Carnegie Mellon University" must not be used to
21 *    endorse or promote products derived from this software without
22 *    prior written permission. For permission or any other legal
23 *    details, please contact
24 *      Office of Technology Transfer
25 *      Carnegie Mellon University
26 *      5000 Forbes Avenue
27 *      Pittsburgh, PA  15213-3890
28 *      (412) 268-4387, fax: (412) 268-7395
29 *      tech-transfer@andrew.cmu.edu
30 *
31 * 4. Redistributions of any form whatsoever must retain the following
32 *    acknowledgment:
33 *    "This product includes software developed by Computing Services
34 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
35 *
36 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
37 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
38 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
39 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
40 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
41 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
42 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
43 */
44
45#include <config.h>
46#ifndef macintosh
47#ifdef WIN32
48# include <winsock.h>
49#else
50# include <sys/param.h>
51# include <sys/socket.h>
52# include <netinet/in.h>
53# include <arpa/inet.h>
54# include <netdb.h>
55#endif /* WIN32 */
56#endif /* macintosh */
57#ifdef HAVE_UNISTD_H
58#include <unistd.h>
59#endif
60#include <fcntl.h>
61#include <sasl.h>
62#include <saslutil.h>
63#include <saslplug.h>
64
65#include <errno.h>
66#include <ctype.h>
67
68#ifdef HAVE_INTTYPES_H
69#include <inttypes.h>
70#endif
71
72#include "plugin_common.h"
73
74/* translate IPv4 mapped IPv6 address to IPv4 address */
75static void sockaddr_unmapped(
76#ifdef IN6_IS_ADDR_V4MAPPED
77  struct sockaddr *sa, socklen_t *len
78#else
79  struct sockaddr *sa __attribute__((unused)),
80  socklen_t *len __attribute__((unused))
81#endif
82)
83{
84#ifdef IN6_IS_ADDR_V4MAPPED
85    struct sockaddr_in6 *sin6;
86    struct sockaddr_in *sin4;
87    uint32_t addr;
88    int port;
89
90    if (sa->sa_family != AF_INET6)
91	return;
92    sin6 = (struct sockaddr_in6 *)sa;
93    if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
94	return;
95    sin4 = (struct sockaddr_in *)sa;
96    addr = *(uint32_t *)&sin6->sin6_addr.s6_addr[12];
97    port = sin6->sin6_port;
98    memset(sin4, 0, sizeof(struct sockaddr_in));
99    sin4->sin_addr.s_addr = addr;
100    sin4->sin_port = port;
101    sin4->sin_family = AF_INET;
102#ifdef HAVE_SOCKADDR_SA_LEN
103    sin4->sin_len = sizeof(struct sockaddr_in);
104#endif
105    *len = sizeof(struct sockaddr_in);
106#else
107    return;
108#endif
109}
110
111int _plug_ipfromstring(const sasl_utils_t *utils, const char *addr,
112		       struct sockaddr *out, socklen_t outlen)
113{
114    int i, j;
115    socklen_t len;
116    struct sockaddr_storage ss;
117    struct addrinfo hints, *ai = NULL;
118    char hbuf[NI_MAXHOST];
119
120    if(!utils || !addr || !out) {
121	if(utils) PARAMERROR( utils );
122	return SASL_BADPARAM;
123    }
124
125    /* Parse the address */
126    for (i = 0; addr[i] != '\0' && addr[i] != ';'; i++) {
127	if (i >= NI_MAXHOST) {
128	    if(utils) PARAMERROR( utils );
129	    return SASL_BADPARAM;
130	}
131	hbuf[i] = addr[i];
132    }
133    hbuf[i] = '\0';
134
135    if (addr[i] == ';')
136	i++;
137    /* XXX/FIXME: Do we need this check? */
138    for (j = i; addr[j] != '\0'; j++)
139	if (!isdigit((int)(addr[j]))) {
140	    PARAMERROR( utils );
141	    return SASL_BADPARAM;
142	}
143
144    memset(&hints, 0, sizeof(hints));
145    hints.ai_family = PF_UNSPEC;
146    hints.ai_socktype = SOCK_STREAM;
147    hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
148
149    if (getaddrinfo(hbuf, &addr[i], &hints, &ai) != 0) {
150	PARAMERROR( utils );
151	return SASL_BADPARAM;
152    }
153
154    len = ai->ai_addrlen;
155    memcpy(&ss, ai->ai_addr, len);
156    freeaddrinfo(ai);
157    sockaddr_unmapped((struct sockaddr *)&ss, &len);
158    if (outlen < len) {
159	PARAMERROR( utils );
160	return SASL_BUFOVER;
161    }
162
163    memcpy(out, &ss, len);
164
165    return SASL_OK;
166}
167
168int _plug_iovec_to_buf(const sasl_utils_t *utils, const struct iovec *vec,
169		       unsigned numiov, buffer_info_t **output)
170{
171    unsigned i;
172    int ret;
173    buffer_info_t *out;
174    unsigned char *pos;
175
176    if(!utils || !vec || !output) {
177	if(utils) PARAMERROR( utils );
178	return SASL_BADPARAM;
179    }
180
181    if(!(*output)) {
182	*output = utils->malloc(sizeof(buffer_info_t));
183	if(!*output) {
184	    MEMERROR(utils);
185	    return SASL_NOMEM;
186	}
187	memset(*output,0,sizeof(buffer_info_t));
188    }
189
190    out = *output;
191
192    out->curlen = 0;
193    for(i=0; i<numiov; i++)
194	out->curlen += vec[i].iov_len;
195
196    ret = _plug_buf_alloc(utils, &out->data, &out->reallen, out->curlen);
197
198    if(ret != SASL_OK) {
199	MEMERROR(utils);
200	return SASL_NOMEM;
201    }
202
203    memset(out->data, 0, out->reallen);
204    pos = out->data;
205
206    for(i=0; i<numiov; i++) {
207		memcpy(pos, vec[i].iov_base, vec[i].iov_len);
208		pos += vec[i].iov_len;
209    }
210
211    return SASL_OK;
212}
213
214/* Basically a conditional call to realloc(), if we need more */
215int _plug_buf_alloc(const sasl_utils_t *utils, unsigned char **rwbuf,
216		    unsigned int *curlen, unsigned int newlen)
217{
218    if(!utils || !rwbuf || !curlen) {
219	PARAMERROR(utils);
220	return SASL_BADPARAM;
221    }
222
223    if(!(*rwbuf)) {
224	*rwbuf = utils->malloc(newlen);
225	if (*rwbuf == NULL) {
226	    *curlen = 0;
227	    MEMERROR(utils);
228	    return SASL_NOMEM;
229	}
230	*curlen = newlen;
231    } else if(*rwbuf && *curlen < newlen) {
232	size_t needed = 2*(*curlen);
233
234	while(needed < newlen)
235	    needed *= 2;
236
237	*rwbuf = utils->realloc(*rwbuf, needed);
238	if (*rwbuf == NULL) {
239	    *curlen = 0;
240	    MEMERROR(utils);
241	    return SASL_NOMEM;
242	}
243	*curlen = needed;
244    }
245
246    return SASL_OK;
247}
248
249/* copy a string */
250int _plug_strdup(const sasl_utils_t * utils, const char *in,
251		 char **out, int *outlen)
252{
253  size_t len = strlen(in);
254
255  if(!utils || !in || !out) {
256      if(utils) PARAMERROR(utils);
257      return SASL_BADPARAM;
258  }
259
260  *out = utils->malloc(len + 1);
261  if (!*out) {
262      MEMERROR(utils);
263      return SASL_NOMEM;
264  }
265
266  strlcpy((char *) *out, in, len+1);
267
268  if (outlen)
269      *outlen = len;
270
271  return SASL_OK;
272}
273
274void _plug_free_string(const sasl_utils_t *utils, char **str)
275{
276  size_t len;
277
278  if (!utils || !str || !(*str)) return;
279
280  len = strlen(*str);
281
282  utils->erasebuffer(*str, len);
283  utils->free(*str);
284
285  *str=NULL;
286}
287
288void _plug_free_secret(const sasl_utils_t *utils, sasl_secret_t **secret)
289{
290    if(!utils || !secret || !(*secret)) return;
291
292    utils->erasebuffer((char *)(*secret)->data, (*secret)->len);
293    utils->free(*secret);
294    *secret = NULL;
295}
296
297/*
298 * Trys to find the prompt with the lookingfor id in the prompt list
299 * Returns it if found. NULL otherwise
300 */
301sasl_interact_t *_plug_find_prompt(sasl_interact_t **promptlist,
302				   unsigned int lookingfor)
303{
304    sasl_interact_t *prompt;
305
306    if (promptlist && *promptlist) {
307	for (prompt = *promptlist; prompt->id != SASL_CB_LIST_END; ++prompt) {
308	    if (prompt->id==lookingfor)
309		return prompt;
310	}
311    }
312
313    return NULL;
314}
315
316/*
317 * Retrieve the simple string given by the callback id.
318 */
319int _plug_get_simple(const sasl_utils_t *utils, unsigned int id,
320		     const char **result, sasl_interact_t **prompt_need)
321{
322
323    int ret;
324    sasl_getsimple_t *simple_cb;
325    void *simple_context;
326    sasl_interact_t *prompt;
327
328    /* see if we were given the result in the prompt */
329    prompt = _plug_find_prompt(prompt_need, id);
330    if (prompt != NULL) {
331	/* We prompted, and got.*/
332
333	if (!prompt->result) {
334	    SETERROR(utils, "Unexpectedly missing a prompt result");
335	    return SASL_BADPARAM;
336	}
337
338	*result = prompt->result;
339	return SASL_OK;
340    }
341
342    /* Try to get the callback... */
343    ret = utils->getcallback(utils->conn, id, &simple_cb, &simple_context);
344
345    if (ret == SASL_OK && simple_cb) {
346	ret = simple_cb(simple_context, id, result, NULL);
347	if (ret != SASL_OK)
348	    return ret;
349
350	if (!*result) {
351	    PARAMERROR(utils);
352	    return SASL_BADPARAM;
353	}
354    }
355
356    return ret;
357}
358
359/*
360 * Retrieve the user password.
361 */
362int _plug_get_password(const sasl_utils_t *utils, sasl_secret_t **password,
363		       unsigned int *iscopy, sasl_interact_t **prompt_need)
364{
365    int ret;
366    sasl_getsecret_t *pass_cb;
367    void *pass_context;
368    sasl_interact_t *prompt;
369
370    *iscopy = 0;
371
372    /* see if we were given the password in the prompt */
373    prompt = _plug_find_prompt(prompt_need, SASL_CB_PASS);
374    if (prompt != NULL) {
375	/* We prompted, and got.*/
376
377	if (!prompt->result) {
378	    SETERROR(utils, "Unexpectedly missing a prompt result");
379	    return SASL_BADPARAM;
380	}
381
382	/* copy what we got into a secret_t */
383	*password = (sasl_secret_t *) utils->malloc(sizeof(sasl_secret_t) +
384						    prompt->len + 1);
385	if (!*password) {
386	    MEMERROR(utils);
387	    return SASL_NOMEM;
388	}
389
390	(*password)->len=prompt->len;
391	memcpy((*password)->data, prompt->result, prompt->len);
392	(*password)->data[(*password)->len]=0;
393
394	*iscopy = 1;
395
396	return SASL_OK;
397    }
398
399    /* Try to get the callback... */
400    ret = utils->getcallback(utils->conn, SASL_CB_PASS,
401			     &pass_cb, &pass_context);
402
403    if (ret == SASL_OK && pass_cb) {
404	ret = pass_cb(utils->conn, pass_context, SASL_CB_PASS, password);
405	if (ret != SASL_OK)
406	    return ret;
407
408	if (!*password) {
409	    PARAMERROR(utils);
410	    return SASL_BADPARAM;
411	}
412    }
413
414    return ret;
415}
416
417/*
418 * Retrieve the string given by the challenge prompt id.
419 */
420int _plug_challenge_prompt(const sasl_utils_t *utils, unsigned int id,
421			   const char *challenge, const char *promptstr,
422			   const char **result, sasl_interact_t **prompt_need)
423{
424    int ret;
425    sasl_chalprompt_t *chalprompt_cb;
426    void *chalprompt_context;
427    sasl_interact_t *prompt;
428
429    /* see if we were given the password in the prompt */
430    prompt = _plug_find_prompt(prompt_need, id);
431    if (prompt != NULL) {
432	/* We prompted, and got.*/
433
434	if (!prompt->result) {
435	    SETERROR(utils, "Unexpectedly missing a prompt result");
436	    return SASL_BADPARAM;
437	}
438
439	*result = prompt->result;
440	return SASL_OK;
441    }
442
443    /* Try to get the callback... */
444    ret = utils->getcallback(utils->conn, id,
445			     &chalprompt_cb, &chalprompt_context);
446
447    if (ret == SASL_OK && chalprompt_cb) {
448	ret = chalprompt_cb(chalprompt_context, id,
449			    challenge, promptstr, NULL, result, NULL);
450	if (ret != SASL_OK)
451	    return ret;
452
453	if (!*result) {
454	    PARAMERROR(utils);
455	    return SASL_BADPARAM;
456	}
457    }
458
459    return ret;
460}
461
462/*
463 * Retrieve the client realm.
464 */
465int _plug_get_realm(const sasl_utils_t *utils, const char **availrealms,
466		    const char **realm, sasl_interact_t **prompt_need)
467{
468    int ret;
469    sasl_getrealm_t *realm_cb;
470    void *realm_context;
471    sasl_interact_t *prompt;
472
473    /* see if we were given the result in the prompt */
474    prompt = _plug_find_prompt(prompt_need, SASL_CB_GETREALM);
475    if (prompt != NULL) {
476	/* We prompted, and got.*/
477
478	if (!prompt->result) {
479	    SETERROR(utils, "Unexpectedly missing a prompt result");
480	    return SASL_BADPARAM;
481	}
482
483	*realm = prompt->result;
484	return SASL_OK;
485    }
486
487    /* Try to get the callback... */
488    ret = utils->getcallback(utils->conn, SASL_CB_GETREALM,
489			     &realm_cb, &realm_context);
490
491    if (ret == SASL_OK && realm_cb) {
492	ret = realm_cb(realm_context, SASL_CB_GETREALM, availrealms, realm);
493	if (ret != SASL_OK)
494	    return ret;
495
496	if (!*realm) {
497	    PARAMERROR(utils);
498	    return SASL_BADPARAM;
499	}
500    }
501
502    return ret;
503}
504
505/*
506 * Make the requested prompts. (prompt==NULL means we don't want it)
507 */
508int _plug_make_prompts(const sasl_utils_t *utils,
509		       sasl_interact_t **prompts_res,
510		       const char *user_prompt, const char *user_def,
511		       const char *auth_prompt, const char *auth_def,
512		       const char *pass_prompt, const char *pass_def,
513		       const char *echo_chal,
514		       const char *echo_prompt, const char *echo_def,
515		       const char *realm_chal,
516		       const char *realm_prompt, const char *realm_def)
517{
518    int num = 1;
519    int alloc_size;
520    sasl_interact_t *prompts;
521
522    if (user_prompt) num++;
523    if (auth_prompt) num++;
524    if (pass_prompt) num++;
525    if (echo_prompt) num++;
526    if (realm_prompt) num++;
527
528    if (num == 1) {
529	SETERROR( utils, "make_prompts() called with no actual prompts" );
530	return SASL_FAIL;
531    }
532
533    alloc_size = sizeof(sasl_interact_t)*num;
534    prompts = utils->malloc(alloc_size);
535    if (!prompts) {
536	MEMERROR( utils );
537	return SASL_NOMEM;
538    }
539    memset(prompts, 0, alloc_size);
540
541    *prompts_res = prompts;
542
543    if (user_prompt) {
544	(prompts)->id = SASL_CB_USER;
545	(prompts)->challenge = "Authorization Name";
546	(prompts)->prompt = user_prompt;
547	(prompts)->defresult = user_def;
548
549	prompts++;
550    }
551
552    if (auth_prompt) {
553	(prompts)->id = SASL_CB_AUTHNAME;
554	(prompts)->challenge = "Authentication Name";
555	(prompts)->prompt = auth_prompt;
556	(prompts)->defresult = auth_def;
557
558	prompts++;
559    }
560
561    if (pass_prompt) {
562	(prompts)->id = SASL_CB_PASS;
563	(prompts)->challenge = "Password";
564	(prompts)->prompt = pass_prompt;
565	(prompts)->defresult = pass_def;
566
567	prompts++;
568    }
569
570    if (echo_prompt) {
571	(prompts)->id = SASL_CB_ECHOPROMPT;
572	(prompts)->challenge = echo_chal;
573	(prompts)->prompt = echo_prompt;
574	(prompts)->defresult = echo_def;
575
576	prompts++;
577    }
578
579    if (realm_prompt) {
580	(prompts)->id = SASL_CB_GETREALM;
581	(prompts)->challenge = realm_chal;
582	(prompts)->prompt = realm_prompt;
583	(prompts)->defresult = realm_def;
584
585	prompts++;
586    }
587
588    /* add the ending one */
589    (prompts)->id = SASL_CB_LIST_END;
590    (prompts)->challenge = NULL;
591    (prompts)->prompt = NULL;
592    (prompts)->defresult = NULL;
593
594    return SASL_OK;
595}
596
597/*
598 * Decode and concatenate multiple packets using the given function
599 * to decode each packet.
600 */
601int _plug_decode(const sasl_utils_t *utils,
602		 void *context,
603		 const unsigned char *input, unsigned inputlen,
604		 unsigned char **output,		/* output buffer */
605		 unsigned int *outputsize,	/* current size of output buffer */
606		 unsigned int *outputlen,	/* length of data in output buffer */
607		 int (*decode_pkt)(void *context,
608				   const unsigned char **input, unsigned int *inputlen,
609				   unsigned char **output, unsigned int *outputlen))
610{
611    unsigned char *tmp = NULL;
612    unsigned tmplen = 0;
613    int ret;
614
615    *outputlen = 0;
616
617    while (inputlen!=0)
618    {
619	/* no need to free tmp */
620      ret = decode_pkt(context, &input, &inputlen, &tmp, &tmplen);
621
622      if(ret != SASL_OK) return ret;
623
624      if (tmp!=NULL) /* if received 2 packets merge them together */
625      {
626	  ret = _plug_buf_alloc(utils, (unsigned char **)output, outputsize, *outputlen + tmplen + 1);
627	  if(ret != SASL_OK) return ret;
628
629	  memcpy(*output + *outputlen, tmp, tmplen);
630
631	  /* Protect stupid clients */
632	  *(*output + *outputlen + tmplen) = '\0';
633
634	  *outputlen+=tmplen;
635      }
636    }
637
638    return SASL_OK;
639}
640
641/* returns the realm we should pretend to be in */
642int _plug_parseuser(const sasl_utils_t *utils,
643		    char **user, char **realm, const char *user_realm,
644		    const char *serverFQDN, const char *input)
645{
646    int ret;
647    char *r;
648
649    if(!user || !serverFQDN) {
650	PARAMERROR( utils );
651	return SASL_BADPARAM;
652    }
653
654    r = strchr(input, '@');
655    if (!r) {
656	/* hmmm, the user didn't specify a realm */
657	if(user_realm && user_realm[0]) {
658	    ret = _plug_strdup(utils, user_realm, realm, NULL);
659	} else {
660	    /* Default to serverFQDN */
661	    ret = _plug_strdup(utils, serverFQDN, realm, NULL);
662	}
663
664	if (ret == SASL_OK) {
665	    ret = _plug_strdup(utils, input, user, NULL);
666	}
667    } else {
668	r++;
669	ret = _plug_strdup(utils, r, realm, NULL);
670	*--r = '\0';
671	*user = utils->malloc(r - input + 1);
672	if (*user) {
673	    strncpy(*user, input, r - input +1);
674	} else {
675	    MEMERROR( utils );
676	    ret = SASL_NOMEM;
677	}
678	*r = '@';
679    }
680
681    return ret;
682}
683