Postfix3.3.1
xsasl_cyrus_client.c
[詳解]
1 /*++
2 /* NAME
3 /* xsasl_cyrus_client 3
4 /* SUMMARY
5 /* Cyrus SASL client-side plug-in
6 /* SYNOPSIS
7 /* #include <xsasl_cyrus_client.h>
8 /*
9 /* XSASL_CLIENT_IMPL *xsasl_cyrus_client_init(client_type, path_info)
10 /* const char *client_type;
11 /* DESCRIPTION
12 /* This module implements the Cyrus SASL client-side authentication
13 /* plug-in.
14 /*
15 /* xsasl_cyrus_client_init() initializes the Cyrus SASL library and
16 /* returns an implementation handle that can be used to generate
17 /* SASL client instances.
18 /*
19 /* Arguments:
20 /* .IP client_type
21 /* The plug-in SASL client type (cyrus). This argument is
22 /* ignored, but it could be used when one implementation
23 /* provides multiple variants.
24 /* .IP path_info
25 /* Implementation-specific information to specify the location
26 /* of a configuration file, rendez-vous point, etc. This
27 /* information is ignored by the Cyrus SASL client plug-in.
28 /* DIAGNOSTICS
29 /* Fatal: out of memory.
30 /*
31 /* Panic: interface violation.
32 /*
33 /* Other: the routines log a warning and return an error result
34 /* as specified in xsasl_client(3).
35 /* SEE ALSO
36 /* xsasl_client(3) Client API
37 /* LICENSE
38 /* .ad
39 /* .fi
40 /* The Secure Mailer license must be distributed with this software.
41 /* AUTHOR(S)
42 /* Original author:
43 /* Till Franke
44 /* SuSE Rhein/Main AG
45 /* 65760 Eschborn, Germany
46 /*
47 /* Adopted by:
48 /* Wietse Venema
49 /* IBM T.J. Watson Research
50 /* P.O. Box 704
51 /* Yorktown Heights, NY 10598, USA
52 /*--*/
53 
54  /*
55  * System library.
56  */
57 #include <sys_defs.h>
58 #include <stdlib.h>
59 #include <string.h>
60 
61  /*
62  * Utility library
63  */
64 #include <msg.h>
65 #include <mymalloc.h>
66 #include <stringops.h>
67 
68  /*
69  * Global library
70  */
71 #include <mail_params.h>
72 
73  /*
74  * Application-specific
75  */
76 #include <xsasl.h>
77 #include <xsasl_cyrus.h>
78 #include <xsasl_cyrus_common.h>
79 
80 #if defined(USE_SASL_AUTH) && defined(USE_CYRUS_SASL)
81 
82 #include <sasl.h>
83 #include <saslutil.h>
84 
85 /*
86  * Silly little macros.
87  */
88 #define STR(s) vstring_str(s)
89 
90  /*
91  * Macros to handle API differences between SASLv1 and SASLv2. Specifics:
92  *
93  * The SASL_LOG_* constants were renamed in SASLv2.
94  *
95  * SASLv2's sasl_client_new takes two new parameters to specify local and
96  * remote IP addresses for auth mechs that use them.
97  *
98  * SASLv2's sasl_client_start function no longer takes the secret parameter.
99  *
100  * SASLv2's sasl_decode64 function takes an extra parameter for the length of
101  * the output buffer.
102  *
103  * The other major change is that SASLv2 now takes more responsibility for
104  * deallocating memory that it allocates internally. Thus, some of the
105  * function parameters are now 'const', to make sure we don't try to free
106  * them too. This is dealt with in the code later on.
107  */
108 #if SASL_VERSION_MAJOR < 2
109 /* SASL version 1.x */
110 #define SASL_CLIENT_NEW(srv, fqdn, lport, rport, prompt, secflags, pconn) \
111  sasl_client_new(srv, fqdn, prompt, secflags, pconn)
112 #define SASL_CLIENT_START(conn, mechlst, secret, prompt, clout, cllen, mech) \
113  sasl_client_start(conn, mechlst, secret, prompt, clout, cllen, mech)
114 #define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
115  sasl_decode64(in, inlen, out, outlen)
116 typedef char *CLIENTOUT_TYPE;
117 
118 #endif
119 
120 #if SASL_VERSION_MAJOR >= 2
121 /* SASL version > 2.x */
122 #define SASL_CLIENT_NEW(srv, fqdn, lport, rport, prompt, secflags, pconn) \
123  sasl_client_new(srv, fqdn, lport, rport, prompt, secflags, pconn)
124 #define SASL_CLIENT_START(conn, mechlst, secret, prompt, clout, cllen, mech) \
125  sasl_client_start(conn, mechlst, prompt, clout, cllen, mech)
126 #define SASL_DECODE64(in, inlen, out, outmaxlen, outlen) \
127  sasl_decode64(in, inlen, out, outmaxlen, outlen)
128 typedef const char *CLIENTOUT_TYPE;
129 
130 #endif
131 
132  /*
133  * The XSASL_CYRUS_CLIENT object is derived from the generic XSASL_CLIENT
134  * object.
135  */
136 typedef struct {
137  XSASL_CLIENT xsasl; /* generic members, must be first */
138  VSTREAM *stream; /* client-server connection */
139  sasl_conn_t *sasl_conn; /* SASL context */
140  VSTRING *decoded; /* decoded server challenge */
141  sasl_callback_t *callbacks; /* user/password lookup */
142  char *username;
143  char *password;
144 } XSASL_CYRUS_CLIENT;
145 
146  /*
147  * Forward declarations.
148  */
149 static void xsasl_cyrus_client_done(XSASL_CLIENT_IMPL *);
150 static XSASL_CLIENT *xsasl_cyrus_client_create(XSASL_CLIENT_IMPL *,
152 static int xsasl_cyrus_client_set_security(XSASL_CLIENT *, const char *);
153 static int xsasl_cyrus_client_first(XSASL_CLIENT *, const char *, const char *,
154  const char *, const char **, VSTRING *);
155 static int xsasl_cyrus_client_next(XSASL_CLIENT *, const char *, VSTRING *);
156 static void xsasl_cyrus_client_free(XSASL_CLIENT *);
157 
158 /* xsasl_cyrus_client_get_user - username lookup call-back routine */
159 
160 static int xsasl_cyrus_client_get_user(void *context, int unused_id,
161  const char **result,
162  unsigned *len)
163 {
164  const char *myname = "xsasl_cyrus_client_get_user";
165  XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) context;
166 
167  if (msg_verbose)
168  msg_info("%s: %s", myname, client->username);
169 
170  /*
171  * Sanity check.
172  */
173  if (client->password == 0)
174  msg_panic("%s: no username looked up", myname);
175 
176  *result = client->username;
177  if (len)
178  *len = strlen(client->username);
179  return (SASL_OK);
180 }
181 
182 /* xsasl_cyrus_client_get_passwd - password lookup call-back routine */
183 
184 static int xsasl_cyrus_client_get_passwd(sasl_conn_t *conn, void *context,
185  int id, sasl_secret_t **psecret)
186 {
187  const char *myname = "xsasl_cyrus_client_get_passwd";
188  XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) context;
189  int len;
190 
191  if (msg_verbose)
192  msg_info("%s: %s", myname, client->password);
193 
194  /*
195  * Sanity check.
196  */
197  if (!conn || !psecret || id != SASL_CB_PASS)
198  return (SASL_BADPARAM);
199  if (client->password == 0)
200  msg_panic("%s: no password looked up", myname);
201 
202  /*
203  * Convert the password into a counted string.
204  */
205  len = strlen(client->password);
206  if ((*psecret = (sasl_secret_t *) malloc(sizeof(sasl_secret_t) + len)) == 0)
207  return (SASL_NOMEM);
208  (*psecret)->len = len;
209  memcpy((*psecret)->data, client->password, len + 1);
210 
211  return (SASL_OK);
212 }
213 
214 /* xsasl_cyrus_client_init - initialize Cyrus SASL library */
215 
216 XSASL_CLIENT_IMPL *xsasl_cyrus_client_init(const char *unused_client_type,
217  const char *unused_path_info)
218 {
219  XSASL_CLIENT_IMPL *xp;
220  int sasl_status;
221 
222  /*
223  * Global callbacks. These have no per-session context.
224  */
225  static sasl_callback_t callbacks[] = {
226  {SASL_CB_LOG, (XSASL_CYRUS_CB) &xsasl_cyrus_log, 0},
227  {SASL_CB_LIST_END, 0, 0}
228  };
229 
230 #if SASL_VERSION_MAJOR >= 2 && (SASL_VERSION_MINOR >= 2 \
231  || (SASL_VERSION_MINOR == 1 && SASL_VERSION_STEP >= 19))
232  int sasl_major;
233  int sasl_minor;
234  int sasl_step;
235 
236  /*
237  * DLL hell guard.
238  */
239  sasl_version_info((const char **) 0, (const char **) 0,
240  &sasl_major, &sasl_minor,
241  &sasl_step, (int *) 0);
242  if (sasl_major != SASL_VERSION_MAJOR
243 #if 0
244  || sasl_minor != SASL_VERSION_MINOR
245  || sasl_step != SASL_VERSION_STEP
246 #endif
247  ) {
248  msg_warn("incorrect SASL library version. "
249  "Postfix was built with include files from version %d.%d.%d, "
250  "but the run-time library version is %d.%d.%d",
251  SASL_VERSION_MAJOR, SASL_VERSION_MINOR, SASL_VERSION_STEP,
252  sasl_major, sasl_minor, sasl_step);
253  return (0);
254  }
255 #endif
256 
257  if (*var_cyrus_conf_path) {
258 #ifdef SASL_PATH_TYPE_CONFIG /* Cyrus SASL 2.1.22 */
259  if (sasl_set_path(SASL_PATH_TYPE_CONFIG,
260  var_cyrus_conf_path) != SASL_OK)
261  msg_warn("failed to set Cyrus SASL configuration path: \"%s\"",
263 #else
264  msg_warn("%s is not empty, but setting the Cyrus SASL configuration "
265  "path is not supported with SASL library version %d.%d.%d",
266  VAR_CYRUS_CONF_PATH, SASL_VERSION_MAJOR,
267  SASL_VERSION_MINOR, SASL_VERSION_STEP);
268 #endif
269  }
270 
271  /*
272  * Initialize the SASL library.
273  */
274  if ((sasl_status = sasl_client_init(callbacks)) != SASL_OK) {
275  msg_warn("SASL library initialization error: %s",
276  xsasl_cyrus_strerror(sasl_status));
277  return (0);
278  }
279 
280  /*
281  * Return a generic XSASL_CLIENT_IMPL object. We don't need to extend it
282  * with our own methods or data.
283  */
284  xp = (XSASL_CLIENT_IMPL *) mymalloc(sizeof(*xp));
285  xp->create = xsasl_cyrus_client_create;
286  xp->done = xsasl_cyrus_client_done;
287  return (xp);
288 }
289 
290 /* xsasl_cyrus_client_done - dispose of implementation */
291 
292 static void xsasl_cyrus_client_done(XSASL_CLIENT_IMPL *impl)
293 {
294  myfree((void *) impl);
295  sasl_done();
296 }
297 
298 /* xsasl_cyrus_client_create - per-session SASL initialization */
299 
300 XSASL_CLIENT *xsasl_cyrus_client_create(XSASL_CLIENT_IMPL *unused_impl,
302 {
303  XSASL_CYRUS_CLIENT *client = 0;
304  static sasl_callback_t callbacks[] = {
305  {SASL_CB_USER, (XSASL_CYRUS_CB) &xsasl_cyrus_client_get_user, 0},
306  {SASL_CB_AUTHNAME, (XSASL_CYRUS_CB) &xsasl_cyrus_client_get_user, 0},
307  {SASL_CB_PASS, (XSASL_CYRUS_CB) &xsasl_cyrus_client_get_passwd, 0},
308  {SASL_CB_LIST_END, 0, 0}
309  };
310  sasl_conn_t *sasl_conn = 0;
311  sasl_callback_t *custom_callbacks = 0;
312  sasl_callback_t *cp;
313  int sasl_status;
314 
315  /*
316  * The optimizer will eliminate code duplication and/or dead code.
317  */
318 #define XSASL_CYRUS_CLIENT_CREATE_ERROR_RETURN(x) \
319  do { \
320  if (client) { \
321  xsasl_cyrus_client_free(&client->xsasl); \
322  } else { \
323  if (custom_callbacks) \
324  myfree((void *) custom_callbacks); \
325  if (sasl_conn) \
326  sasl_dispose(&sasl_conn); \
327  } \
328  return (x); \
329  } while (0)
330 
331  /*
332  * Per-session initialization. Provide each session with its own callback
333  * context.
334  */
335 #define NULL_SECFLAGS 0
336 
337  custom_callbacks = (sasl_callback_t *) mymalloc(sizeof(callbacks));
338  memcpy((void *) custom_callbacks, callbacks, sizeof(callbacks));
339 
340 #define NULL_SERVER_ADDR ((char *) 0)
341 #define NULL_CLIENT_ADDR ((char *) 0)
342 
343  if ((sasl_status = SASL_CLIENT_NEW(args->service, args->server_name,
344  NULL_CLIENT_ADDR, NULL_SERVER_ADDR,
345  var_cyrus_sasl_authzid ? custom_callbacks :
346  custom_callbacks + 1, NULL_SECFLAGS,
347  &sasl_conn)) != SASL_OK) {
348  msg_warn("per-session SASL client initialization: %s",
349  xsasl_cyrus_strerror(sasl_status));
350  XSASL_CYRUS_CLIENT_CREATE_ERROR_RETURN(0);
351  }
352 
353  /*
354  * Extend the XSASL_CLIENT object with our own state. We use long-lived
355  * conversion buffers rather than local variables to avoid memory leaks
356  * in case of read/write timeout or I/O error.
357  *
358  * XXX If we enable SASL encryption, there needs to be a way to inform the
359  * application, so that they can turn off connection caching, refuse
360  * STARTTLS, etc.
361  */
362  client = (XSASL_CYRUS_CLIENT *) mymalloc(sizeof(*client));
363  client->xsasl.free = xsasl_cyrus_client_free;
364  client->xsasl.first = xsasl_cyrus_client_first;
365  client->xsasl.next = xsasl_cyrus_client_next;
366  client->stream = args->stream;
367  client->sasl_conn = sasl_conn;
368  client->callbacks = custom_callbacks;
369  client->decoded = vstring_alloc(20);
370  client->username = 0;
371  client->password = 0;
372 
373  for (cp = custom_callbacks; cp->id != SASL_CB_LIST_END; cp++)
374  cp->context = (void *) client;
375 
376  if (xsasl_cyrus_client_set_security(&client->xsasl,
377  args->security_options)
378  != XSASL_AUTH_OK)
379  XSASL_CYRUS_CLIENT_CREATE_ERROR_RETURN(0);
380 
381  return (&client->xsasl);
382 }
383 
384 /* xsasl_cyrus_client_set_security - set security properties */
385 
386 static int xsasl_cyrus_client_set_security(XSASL_CLIENT *xp,
387  const char *sasl_opts_val)
388 {
389  XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
390  sasl_security_properties_t sec_props;
391  int sasl_status;
392 
393  /*
394  * Per-session security properties. XXX This routine is not sufficiently
395  * documented. What is the purpose of all this?
396  */
397  memset(&sec_props, 0, sizeof(sec_props));
398  sec_props.min_ssf = 0;
399  sec_props.max_ssf = 0; /* don't allow real SASL
400  * security layer */
401  if (*sasl_opts_val == 0) {
402  sec_props.security_flags = 0;
403  } else {
404  sec_props.security_flags =
405  xsasl_cyrus_security_parse_opts(sasl_opts_val);
406  if (sec_props.security_flags == 0) {
407  msg_warn("bad per-session SASL security properties");
408  return (XSASL_AUTH_FAIL);
409  }
410  }
411  sec_props.maxbufsize = 0;
412  sec_props.property_names = 0;
413  sec_props.property_values = 0;
414  if ((sasl_status = sasl_setprop(client->sasl_conn, SASL_SEC_PROPS,
415  &sec_props)) != SASL_OK) {
416  msg_warn("set per-session SASL security properties: %s",
417  xsasl_cyrus_strerror(sasl_status));
418  return (XSASL_AUTH_FAIL);
419  }
420  return (XSASL_AUTH_OK);
421 }
422 
423 /* xsasl_cyrus_client_first - run authentication protocol */
424 
425 static int xsasl_cyrus_client_first(XSASL_CLIENT *xp,
426  const char *mechanism_list,
427  const char *username,
428  const char *password,
429  const char **mechanism,
430  VSTRING *init_resp)
431 {
432  const char *myname = "xsasl_cyrus_client_first";
433  XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
434  unsigned enc_length;
435  unsigned enc_length_out;
436  CLIENTOUT_TYPE clientout;
437  unsigned clientoutlen;
438  int sasl_status;
439 
440 #define NO_SASL_SECRET 0
441 #define NO_SASL_INTERACTION 0
442 
443  /*
444  * Save the username and password for the call-backs.
445  */
446  if (client->username)
447  myfree(client->username);
448  client->username = mystrdup(username);
449  if (client->password)
450  myfree(client->password);
451  client->password = mystrdup(password);
452 
453  /*
454  * Start the client side authentication protocol.
455  */
456  sasl_status = SASL_CLIENT_START((sasl_conn_t *) client->sasl_conn,
457  mechanism_list,
458  NO_SASL_SECRET, NO_SASL_INTERACTION,
459  &clientout, &clientoutlen, mechanism);
460  if (sasl_status != SASL_OK && sasl_status != SASL_CONTINUE) {
461  vstring_strcpy(init_resp, xsasl_cyrus_strerror(sasl_status));
462  return (XSASL_AUTH_FAIL);
463  }
464 
465  /*
466  * Generate the AUTH command and the optional initial client response.
467  * sasl_encode64() produces four bytes for each complete or incomplete
468  * triple of input bytes. Allocate an extra byte for string termination.
469  */
470 #define ENCODE64_LENGTH(n) ((((n) + 2) / 3) * 4)
471 
472  if (clientoutlen > 0) {
473  if (msg_verbose) {
474  escape(client->decoded, clientout, clientoutlen);
475  msg_info("%s: uncoded initial reply: %s",
476  myname, STR(client->decoded));
477  }
478  enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
479  VSTRING_RESET(init_resp); /* Fix 200512 */
480  VSTRING_SPACE(init_resp, enc_length);
481  if ((sasl_status = sasl_encode64(clientout, clientoutlen,
482  STR(init_resp),
483  vstring_avail(init_resp),
484  &enc_length_out)) != SASL_OK)
485  msg_panic("%s: sasl_encode64 botch: %s",
486  myname, xsasl_cyrus_strerror(sasl_status));
487  VSTRING_AT_OFFSET(init_resp, enc_length_out); /* XXX */
488 #if SASL_VERSION_MAJOR < 2
489  /* SASL version 1 doesn't free memory that it allocates. */
490  free(clientout);
491 #endif
492  } else {
493  vstring_strcpy(init_resp, "");
494  }
495  return (XSASL_AUTH_OK);
496 }
497 
498 /* xsasl_cyrus_client_next - continue authentication */
499 
500 static int xsasl_cyrus_client_next(XSASL_CLIENT *xp, const char *server_reply,
501  VSTRING *client_reply)
502 {
503  const char *myname = "xsasl_cyrus_client_next";
504  XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
505  unsigned enc_length;
506  unsigned enc_length_out;
507  CLIENTOUT_TYPE clientout;
508  unsigned clientoutlen;
509  unsigned serverinlen;
510  int sasl_status;
511 
512  /*
513  * Process a server challenge.
514  */
515  serverinlen = strlen(server_reply);
516  VSTRING_RESET(client->decoded); /* Fix 200512 */
517  VSTRING_SPACE(client->decoded, serverinlen);
518  if ((sasl_status = SASL_DECODE64(server_reply, serverinlen,
519  STR(client->decoded),
520  vstring_avail(client->decoded),
521  &enc_length)) != SASL_OK) {
522  vstring_strcpy(client_reply, xsasl_cyrus_strerror(sasl_status));
523  return (XSASL_AUTH_FORM);
524  }
525  if (msg_verbose)
526  msg_info("%s: decoded challenge: %.*s",
527  myname, (int) enc_length, STR(client->decoded));
528  sasl_status = sasl_client_step(client->sasl_conn, STR(client->decoded),
529  enc_length, NO_SASL_INTERACTION,
530  &clientout, &clientoutlen);
531  if (sasl_status != SASL_OK && sasl_status != SASL_CONTINUE) {
532  vstring_strcpy(client_reply, xsasl_cyrus_strerror(sasl_status));
533  return (XSASL_AUTH_FAIL);
534  }
535 
536  /*
537  * Send a client response.
538  */
539  if (clientoutlen > 0) {
540  if (msg_verbose)
541  msg_info("%s: uncoded client response %.*s",
542  myname, (int) clientoutlen, clientout);
543  enc_length = ENCODE64_LENGTH(clientoutlen) + 1;
544  VSTRING_RESET(client_reply); /* Fix 200512 */
545  VSTRING_SPACE(client_reply, enc_length);
546  if ((sasl_status = sasl_encode64(clientout, clientoutlen,
547  STR(client_reply),
548  vstring_avail(client_reply),
549  &enc_length_out)) != SASL_OK)
550  msg_panic("%s: sasl_encode64 botch: %s",
551  myname, xsasl_cyrus_strerror(sasl_status));
552 #if SASL_VERSION_MAJOR < 2
553  /* SASL version 1 doesn't free memory that it allocates. */
554  free(clientout);
555 #endif
556  } else {
557  /* XXX Can't happen. */
558  vstring_strcpy(client_reply, "");
559  }
560  return (XSASL_AUTH_OK);
561 }
562 
563 /* xsasl_cyrus_client_free - per-session cleanup */
564 
565 void xsasl_cyrus_client_free(XSASL_CLIENT *xp)
566 {
567  XSASL_CYRUS_CLIENT *client = (XSASL_CYRUS_CLIENT *) xp;
568 
569  if (client->username)
570  myfree(client->username);
571  if (client->password)
572  myfree(client->password);
573  if (client->sasl_conn)
574  sasl_dispose(&client->sasl_conn);
575  myfree((void *) client->callbacks);
576  vstring_free(client->decoded);
577  myfree((void *) client);
578 }
579 
580 #endif
int msg_verbose
Definition: msg.c:177
void myfree(void *ptr)
Definition: mymalloc.c:207
#define XSASL_AUTH_FAIL
Definition: xsasl.h:127
char * mystrdup(const char *str)
Definition: mymalloc.c:225
#define VAR_CYRUS_CONF_PATH
Definition: mail_params.h:1631
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
void(* done)(struct XSASL_CLIENT_IMPL *)
Definition: xsasl.h:107
VSTRING * escape(VSTRING *, const char *, ssize_t)
Definition: unescape.c:133
const char * service
Definition: xsasl.h:100
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
XSASL_CLIENT *(* create)(struct XSASL_CLIENT_IMPL *, XSASL_CLIENT_CREATE_ARGS *)
Definition: xsasl.h:106
char * var_cyrus_conf_path
Definition: smtp.c:940
#define VSTRING_RESET(vp)
Definition: vstring.h:77
#define STR(x)
Definition: anvil.c:518
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
const char * username(void)
Definition: username.c:38
#define XSASL_AUTH_OK
Definition: xsasl.h:123
int var_cyrus_sasl_authzid
Definition: mail_params.c:332
#define vstring_avail(vp)
Definition: vstring.h:86
const char * security_options
Definition: xsasl.h:102
#define VSTRING_SPACE(vp, len)
Definition: vstring.h:70
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define VSTRING_AT_OFFSET(vp, offset)
Definition: vstring.h:92
#define XSASL_AUTH_FORM
Definition: xsasl.h:126
VSTREAM * stream
Definition: xsasl.h:99
const char * server_name
Definition: xsasl.h:101
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
void msg_info(const char *fmt,...)
Definition: msg.c:199