1/* seterror.c - sasl_seterror split out because glue libraries
2 *              can't pass varargs lists
3 * Rob Siemborski
4 * Tim Martin
5 * split from common.c by Rolf Braun
6 * $Id: seterror.c,v 1.5 2006/02/03 22:33:14 snsimon Exp $
7 */
8
9/*
10 * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 *
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 *
19 * 2. Redistributions in binary form must reproduce the above copyright
20 *    notice, this list of conditions and the following disclaimer in
21 *    the documentation and/or other materials provided with the
22 *    distribution.
23 *
24 * 3. The name "Carnegie Mellon University" must not be used to
25 *    endorse or promote products derived from this software without
26 *    prior written permission. For permission or any other legal
27 *    details, please contact
28 *      Office of Technology Transfer
29 *      Carnegie Mellon University
30 *      5000 Forbes Avenue
31 *      Pittsburgh, PA  15213-3890
32 *      (412) 268-4387, fax: (412) 268-7395
33 *      tech-transfer@andrew.cmu.edu
34 *
35 * 4. Redistributions of any form whatsoever must retain the following
36 *    acknowledgment:
37 *    "This product includes software developed by Computing Services
38 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
39 *
40 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
41 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
42 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
43 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
44 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
45 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
46 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
47 */
48
49#include <config.h>
50#include <stdio.h>
51#include <string.h>
52#include <stdlib.h>
53#include <limits.h>
54#ifdef HAVE_SYSLOG
55#include <syslog.h>
56#endif
57#include <stdarg.h>
58#include <ctype.h>
59
60#include <sasl.h>
61#include <saslutil.h>
62#include <saslplug.h>
63#include "saslint.h"
64
65#ifdef WIN32
66/* need to handle the fact that errno has been defined as a function
67   in a dll, not an extern int */
68# ifdef errno
69#  undef errno
70# endif /* errno */
71#endif /* WIN32 */
72#ifdef HAVE_UNISTD_H
73#include <unistd.h>
74#endif
75
76/* this is apparently no longer a user function */
77static int _sasl_seterror_usererr(int saslerr)
78{
79    /* Hide the difference in a username failure and a password failure */
80    if (saslerr == SASL_NOUSER)
81	return SASL_BADAUTH;
82
83    /* otherwise return the error given; no transform necessary */
84    return saslerr;
85}
86
87/* set the error string which will be returned by sasl_errdetail() using
88 *  syslog()-style formatting (e.g. printf-style with %m as the string form
89 *  of an errno error)
90 *
91 *  primarily for use by server callbacks such as the sasl_authorize_t
92 *  callback and internally to plug-ins
93 *
94 * This will also trigger a call to the SASL logging callback (if any)
95 * with a level of SASL_LOG_FAIL unless the SASL_NOLOG flag is set.
96 *
97 * Messages should be sensitive to the current language setting.  If there
98 * is no SASL_CB_LANGUAGE callback messages MUST be US-ASCII otherwise UTF-8
99 * is used and use of RFC 2482 for mixed-language text is encouraged.
100 *
101 * if conn is NULL, function does nothing
102 */
103void sasl_seterror(sasl_conn_t *conn,
104		   unsigned flags,
105		   const char *fmt, ...)
106{
107  size_t outlen=0; /* current length of output buffer */
108  size_t pos = 0; /* current position in format string */
109  size_t formatlen;
110  int result;
111  sasl_log_t *log_cb = NULL;
112  void *log_ctx;
113  int ival;
114  char *cval;
115  va_list ap; /* varargs thing */
116  char **error_buf;
117  size_t *error_buf_len;
118
119  if(!conn) {
120#ifndef SASL_OSX_CFMGLUE
121      if(!(flags & SASL_NOLOG)) {
122	  /* See if we have a logging callback... */
123	  result = _sasl_getcallback(NULL, SASL_CB_LOG, &log_cb, &log_ctx);
124	  if (result == SASL_OK && ! log_cb)
125	      result = SASL_FAIL;
126	  if (result != SASL_OK)
127	      return;
128
129	  log_cb(log_ctx, SASL_LOG_FAIL,
130		 "No sasl_conn_t passed to sasl_seterror");
131      }
132#endif /* SASL_OSX_CFMGLUE */
133      return;
134  } else if(!fmt) return;
135
136/* we need to use a back end function to get the buffer because the
137   cfm glue can't be rooting around in the internal structs */
138  _sasl_get_errorbuf(conn, &error_buf, &error_buf_len);
139
140  formatlen = strlen(fmt);
141
142  va_start(ap, fmt); /* start varargs */
143
144  while(pos<formatlen)
145  {
146    if (fmt[pos]!='%') /* regular character */
147    {
148      result = _buf_alloc((void **)error_buf, error_buf_len, outlen+1);
149      if (result != SASL_OK)
150	return;
151      (*error_buf)[outlen]=fmt[pos];
152      outlen++;
153      pos++;
154    } else { /* formating thing */
155      int done=0;
156      char frmt[10];
157      int frmtpos=1;
158      char tempbuf[21];
159      frmt[0]='%';
160      pos++;
161
162      while (done==0)
163      {
164	switch(fmt[pos])
165	  {
166	  case 's': /* need to handle this */
167	    cval = va_arg(ap, char *); /* get the next arg */
168	    result = _sasl_add_string(error_buf, error_buf_len,
169				      &outlen, cval);
170
171	    if (result != SASL_OK) /* add the string */
172	      return;
173
174	    done=1;
175	    break;
176
177	  case '%': /* double % output the '%' character */
178	    result = _buf_alloc((void **)error_buf, error_buf_len, outlen+1);
179	    if (result != SASL_OK)
180	      return;
181	    (*error_buf)[outlen]='%';
182	    outlen++;
183	    done=1;
184	    break;
185
186	  case 'm': /* insert the errno string */
187	    result = _sasl_add_string(error_buf, error_buf_len,
188				      &outlen,
189				      strerror(va_arg(ap, int)));
190	    if (result != SASL_OK)
191	      return;
192	    done=1;
193	    break;
194
195	  case 'z': /* insert the sasl error string */
196	    result = _sasl_add_string(error_buf, error_buf_len,	&outlen,
197			 (char *)sasl_errstring(_sasl_seterror_usererr(
198					        va_arg(ap, int)),NULL,NULL));
199	    if (result != SASL_OK)
200	      return;
201	    done=1;
202	    break;
203
204	  case 'c':
205	    frmt[frmtpos++]=fmt[pos];
206	    frmt[frmtpos]=0;
207	    tempbuf[0] = (char) va_arg(ap, int); /* get the next arg */
208	    tempbuf[1]='\0';
209
210	    /* now add the character */
211	    result = _sasl_add_string(error_buf, error_buf_len,
212				      &outlen, tempbuf);
213	    if (result != SASL_OK)
214	      return;
215	    done=1;
216	    break;
217
218	  case 'd':
219	  case 'i':
220	    frmt[frmtpos++]=fmt[pos];
221	    frmt[frmtpos]=0;
222	    ival = va_arg(ap, int); /* get the next arg */
223
224	    snprintf(tempbuf,20,frmt,ival); /* have snprintf do the work */
225	    /* now add the string */
226	    result = _sasl_add_string(error_buf, error_buf_len,
227				      &outlen, tempbuf);
228	    if (result != SASL_OK)
229	      return;
230	    done=1;
231
232	    break;
233	  default:
234	    frmt[frmtpos++]=fmt[pos]; /* add to the formating */
235	    frmt[frmtpos]=0;
236	    if (frmtpos>9)
237	      done=1;
238	  }
239	pos++;
240	if (pos>formatlen)
241	  done=1;
242      }
243
244    }
245  }
246
247  (*error_buf)[outlen]='\0'; /* put 0 at end */
248
249  va_end(ap);
250
251#ifndef SASL_OSX_CFMGLUE
252  if(!(flags & SASL_NOLOG)) {
253      /* See if we have a logging callback... */
254      result = _sasl_getcallback(conn, SASL_CB_LOG, &log_cb, &log_ctx);
255      if (result == SASL_OK && ! log_cb)
256	  result = SASL_FAIL;
257      if (result != SASL_OK)
258	  return;
259
260      result = log_cb(log_ctx, SASL_LOG_FAIL, conn->error_buf);
261  }
262#endif /* SASL_OSX_CFMGLUE */
263}
264