1 /*
   2  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
   3  * Use is subject to license terms.
   4  */
   5 #pragma ident   "%Z%%M% %I%     %E% SMI"
   6 
   7 /* SASL server API implementation
   8  * Rob Siemborski
   9  * Tim Martin
  10  * $Id: client.c,v 1.61 2003/04/16 19:36:00 rjs3 Exp $
  11  */
  12 /* 
  13  * Copyright (c) 1998-2003 Carnegie Mellon University.  All rights reserved.
  14  *
  15  * Redistribution and use in source and binary forms, with or without
  16  * modification, are permitted provided that the following conditions
  17  * are met:
  18  *
  19  * 1. Redistributions of source code must retain the above copyright
  20  *    notice, this list of conditions and the following disclaimer. 
  21  *
  22  * 2. Redistributions in binary form must reproduce the above copyright
  23  *    notice, this list of conditions and the following disclaimer in
  24  *    the documentation and/or other materials provided with the
  25  *    distribution.
  26  *
  27  * 3. The name "Carnegie Mellon University" must not be used to
  28  *    endorse or promote products derived from this software without
  29  *    prior written permission. For permission or any other legal
  30  *    details, please contact  
  31  *      Office of Technology Transfer
  32  *      Carnegie Mellon University
  33  *      5000 Forbes Avenue
  34  *      Pittsburgh, PA  15213-3890
  35  *      (412) 268-4387, fax: (412) 268-7395
  36  *      tech-transfer@andrew.cmu.edu
  37  *
  38  * 4. Redistributions of any form whatsoever must retain the following
  39  *    acknowledgment:
  40  *    "This product includes software developed by Computing Services
  41  *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
  42  *
  43  * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
  44  * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  45  * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
  46  * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  47  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  48  * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  49  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  50  */
  51 
  52 #include <config.h>
  53 #include <stdio.h>
  54 #include <stdlib.h>
  55 #include <limits.h>
  56 #include <ctype.h>
  57 #include <string.h>
  58 #ifdef HAVE_UNISTD_H
  59 #include <unistd.h>
  60 #endif
  61 
  62 /* SASL Headers */
  63 #include "sasl.h"
  64 #include "saslplug.h"
  65 #include "saslutil.h"
  66 #include "saslint.h"
  67 
  68 #ifdef _SUN_SDK_
  69 DEFINE_STATIC_MUTEX(init_client_mutex);
  70 DEFINE_STATIC_MUTEX(client_active_mutex);
  71 /*
  72  * client_plug_mutex ensures only one client plugin is init'ed at a time
  73  * If a plugin is loaded more than once, the glob_context may be overwritten
  74  * which may lead to a memory leak. We keep glob_context with each mech
  75  * to avoid this problem.
  76  */
  77 DEFINE_STATIC_MUTEX(client_plug_mutex);
  78 #else
  79 static cmech_list_t *cmechlist; /* global var which holds the list */
  80 
  81 static sasl_global_callbacks_t global_callbacks;
  82 
  83 static int _sasl_client_active = 0;
  84 #endif /* _SUN_SDK_ */
  85 
  86 #ifdef _SUN_SDK_
  87 static int init_mechlist(_sasl_global_context_t *gctx)
  88 {
  89   cmech_list_t *cmechlist = gctx->cmechlist;
  90 #else
  91 static int init_mechlist()
  92 {
  93 #endif /* _SUN_SDK_ */
  94 
  95   cmechlist->mutex = sasl_MUTEX_ALLOC();
  96   if(!cmechlist->mutex) return SASL_FAIL;
  97   
  98 #ifdef _SUN_SDK_
  99   cmechlist->utils=
 100         _sasl_alloc_utils(gctx, NULL, &gctx->client_global_callbacks);
 101 #else
 102   cmechlist->utils=_sasl_alloc_utils(NULL, &global_callbacks);
 103 #endif /* _SUN_SDK_ */
 104   if (cmechlist->utils==NULL)
 105     return SASL_NOMEM;
 106 
 107   cmechlist->mech_list=NULL;
 108   cmechlist->mech_length=0;
 109 
 110   return SASL_OK;
 111 }
 112 
 113 #ifdef _SUN_SDK_
 114 static int client_done(_sasl_global_context_t *gctx) {
 115   cmech_list_t *cmechlist = gctx->cmechlist;
 116   _sasl_path_info_t *path_info, *p;
 117 #else
 118 static int client_done(void) {
 119 #endif /* _SUN_SDK_ */
 120   cmechanism_t *cm;
 121   cmechanism_t *cprevm;
 122 
 123 #ifdef _SUN_SDK_
 124   if(!gctx->sasl_client_active)
 125       return SASL_NOTINIT;
 126   if (LOCK_MUTEX(&client_active_mutex) < 0) {
 127         return (SASL_FAIL);
 128   }
 129   gctx->sasl_client_active--;
 130 
 131   if(gctx->sasl_client_active) {
 132       /* Don't de-init yet! Our refcount is nonzero. */
 133       UNLOCK_MUTEX(&client_active_mutex);
 134       return SASL_CONTINUE;
 135   }
 136 #else
 137   if(!_sasl_client_active)
 138       return SASL_NOTINIT;
 139   else
 140       _sasl_client_active--;
 141   
 142   if(_sasl_client_active) {
 143       /* Don't de-init yet! Our refcount is nonzero. */
 144       return SASL_CONTINUE;
 145   }
 146 #endif /* _SUN_SDK_ */
 147   
 148   cm=cmechlist->mech_list; /* m point to begging of the list */
 149   while (cm!=NULL)
 150   {
 151     cprevm=cm;
 152     cm=cm->next;
 153 
 154     if (cprevm->plug->mech_free) {
 155 #ifdef _SUN_SDK_
 156         cprevm->plug->mech_free(cprevm->glob_context, cmechlist->utils);
 157 #else
 158         cprevm->plug->mech_free(cprevm->plug->glob_context,
 159                                 cmechlist->utils);
 160 #endif /* _SUN_SDK_ */
 161     }
 162 
 163     sasl_FREE(cprevm->plugname);
 164     sasl_FREE(cprevm);    
 165   }
 166   sasl_MUTEX_FREE(cmechlist->mutex);
 167   _sasl_free_utils(&cmechlist->utils);
 168   sasl_FREE(cmechlist);
 169 
 170 #ifdef _SUN_SDK_
 171   gctx->cmechlist = NULL;
 172   p = gctx->cplug_path_info;
 173   while((path_info = p) != NULL) {
 174     sasl_FREE(path_info->path);
 175     p = path_info->next;
 176     sasl_FREE(path_info);
 177   }
 178   gctx->cplug_path_info = NULL;
 179   UNLOCK_MUTEX(&client_active_mutex);
 180 #else
 181   cmechlist = NULL;
 182 #endif /* _SUN_SDK_ */
 183 
 184   return SASL_OK;
 185 }
 186 
 187 int sasl_client_add_plugin(const char *plugname,
 188                            sasl_client_plug_init_t *entry_point)
 189 {
 190 #ifdef _SUN_SDK_
 191     return (_sasl_client_add_plugin(_sasl_gbl_ctx(), plugname, entry_point));
 192 }
 193 
 194 int _sasl_client_add_plugin(void *ctx,
 195                             const char *plugname,
 196                             sasl_client_plug_init_t *entry_point)
 197 {
 198   cmech_list_t *cmechlist;
 199 #ifdef _INTEGRATED_SOLARIS_
 200   _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
 201   int sun_reg;
 202 #endif /* _INTEGRATED_SOLARIS_ */
 203   int i;
 204   cmechanism_t *m;
 205 #endif /* _SUN_SDK_ */
 206   int plugcount;
 207   sasl_client_plug_t *pluglist;
 208   cmechanism_t *mech;
 209   int result;
 210   int version;
 211   int lupe;
 212 
 213   if(!plugname || !entry_point) return SASL_BADPARAM;
 214   
 215 #ifdef _SUN_SDK_
 216   cmechlist = gctx->cmechlist;
 217 
 218   if (cmechlist == NULL) return SASL_BADPARAM;
 219 
 220   /* Check to see if this plugin has already been registered */
 221   m = cmechlist->mech_list;
 222   for (i = 0; i < cmechlist->mech_length; i++) {
 223     if (strcmp(plugname, m->plugname) == 0) {
 224         return SASL_OK;
 225     }
 226     m = m->next;
 227   }
 228 
 229   result = LOCK_MUTEX(&client_plug_mutex);
 230   if (result != SASL_OK)
 231         return result;
 232 
 233 #endif /* _SUN_SDK_ */
 234 
 235   result = entry_point(cmechlist->utils, SASL_CLIENT_PLUG_VERSION, &version,
 236                        &pluglist, &plugcount);
 237 
 238 #ifdef _INTEGRATED_SOLARIS_
 239   sun_reg = _is_sun_reg(pluglist);
 240 #endif /* _INTEGRATED_SOLARIS_ */
 241   if (result != SASL_OK)
 242   {
 243 #ifdef _SUN_SDK_
 244     UNLOCK_MUTEX(&client_plug_mutex);
 245     __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_WARN,
 246               "entry_point failed in sasl_client_add_plugin for %s",
 247               plugname);
 248 #else
 249     _sasl_log(NULL, SASL_LOG_WARN,
 250               "entry_point failed in sasl_client_add_plugin for %s",
 251               plugname);
 252 #endif /* _SUN_SDK_ */
 253     return result;
 254   }
 255 
 256   if (version != SASL_CLIENT_PLUG_VERSION)
 257   {
 258 #ifdef _SUN_SDK_
 259     UNLOCK_MUTEX(&client_plug_mutex);
 260     __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_WARN,
 261               "version conflict in sasl_client_add_plugin for %s", plugname);
 262 #else
 263     _sasl_log(NULL, SASL_LOG_WARN,
 264               "version conflict in sasl_client_add_plugin for %s", plugname);
 265 #endif /* _SUN_SDK_ */
 266     return SASL_BADVERS;
 267   }
 268 
 269 #ifdef _SUN_SDK_
 270     /* Check plugins to make sure mech_name is non-NULL */
 271     for (lupe=0;lupe < plugcount ;lupe++) {
 272         if (pluglist[lupe].mech_name == NULL)
 273              break;
 274     }
 275     if (lupe < plugcount) {
 276         UNLOCK_MUTEX(&client_plug_mutex);
 277         __sasl_log(gctx, gctx->client_global_callbacks.callbacks,
 278                 SASL_LOG_ERR, "invalid client plugin %s", plugname);
 279         return SASL_BADPROT;
 280     }
 281 #endif /* _SUN_SDK_ */
 282 
 283   for (lupe=0;lupe< plugcount ;lupe++)
 284     {
 285       mech = sasl_ALLOC(sizeof(cmechanism_t));
 286 #ifdef _SUN_SDK_
 287       if (! mech) {
 288         UNLOCK_MUTEX(&client_plug_mutex);
 289         return SASL_NOMEM;
 290       }
 291       mech->glob_context = pluglist->glob_context;
 292 #else
 293       if (! mech) return SASL_NOMEM;
 294 #endif /* _SUN_SDK_ */
 295 
 296       mech->plug=pluglist++;
 297       if(_sasl_strdup(plugname, &mech->plugname, NULL) != SASL_OK) {
 298 #ifdef _SUN_SDK_
 299         UNLOCK_MUTEX(&client_plug_mutex);
 300 #endif /* _SUN_SDK_ */
 301         sasl_FREE(mech);
 302         return SASL_NOMEM;
 303       }
 304 #ifdef _INTEGRATED_SOLARIS_
 305       mech->sun_reg = sun_reg;
 306 #endif /* _INTEGRATED_SOLARIS_ */
 307       mech->version = version;
 308       mech->next = cmechlist->mech_list;
 309       cmechlist->mech_list = mech;
 310       cmechlist->mech_length++;
 311     }
 312 #ifdef _SUN_SDK_
 313     UNLOCK_MUTEX(&client_plug_mutex);
 314 #endif /* _SUN_SDK_ */
 315 
 316   return SASL_OK;
 317 }
 318 
 319 static int
 320 client_idle(sasl_conn_t *conn)
 321 {
 322   cmechanism_t *m;
 323 #ifdef _SUN_SDK_
 324   _sasl_global_context_t *gctx = conn == NULL ? _sasl_gbl_ctx() : conn->gctx;
 325    cmech_list_t *cmechlist = gctx->cmechlist;
 326 #endif /* _SUN_SDK_ */
 327 
 328   if (! cmechlist)
 329     return 0;
 330 
 331   for (m = cmechlist->mech_list;
 332        m;
 333        m = m->next)
 334     if (m->plug->idle
 335 #ifdef _SUN_SDK_
 336         &&  m->plug->idle(m->glob_context,
 337 #else
 338         &&  m->plug->idle(m->plug->glob_context,
 339 #endif /* _SUN_SDK_ */
 340                           conn,
 341                           conn ? ((sasl_client_conn_t *)conn)->cparams : NULL))
 342       return 1;
 343   return 0;
 344 }
 345 
 346 #ifdef _SUN_SDK_
 347 static int _load_client_plugins(_sasl_global_context_t *gctx)
 348 {
 349     int ret;
 350     const add_plugin_list_t _ep_list[] = {
 351       { "sasl_client_plug_init", (add_plugin_t *)_sasl_client_add_plugin },
 352       { "sasl_canonuser_init", (add_plugin_t *)_sasl_canonuser_add_plugin },
 353       { NULL, NULL }
 354     };
 355     const sasl_callback_t *callbacks = gctx->client_global_callbacks.callbacks;
 356 
 357     ret = _sasl_load_plugins(gctx, 0, _ep_list,
 358                              _sasl_find_getpath_callback(callbacks),
 359                              _sasl_find_verifyfile_callback(callbacks));
 360     return (ret);
 361 }
 362 #endif /* _SUN_SDK_ */
 363 
 364 /* initialize the SASL client drivers
 365  *  callbacks      -- base callbacks for all client connections
 366  * returns:
 367  *  SASL_OK        -- Success
 368  *  SASL_NOMEM     -- Not enough memory
 369  *  SASL_BADVERS   -- Mechanism version mismatch
 370  *  SASL_BADPARAM  -- error in config file
 371  *  SASL_NOMECH    -- No mechanisms available
 372  *  ...
 373  */
 374 
 375 int sasl_client_init(const sasl_callback_t *callbacks)
 376 {
 377 #ifdef _SUN_SDK_
 378         return _sasl_client_init(NULL, callbacks);
 379 }
 380 
 381 int _sasl_client_init(void *ctx,
 382                       const sasl_callback_t *callbacks)
 383 {
 384   int ret;
 385   _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
 386 
 387   if (gctx == NULL)
 388         gctx = _sasl_gbl_ctx();
 389 
 390   ret = LOCK_MUTEX(&init_client_mutex);
 391   if (ret < 0) {
 392         return (SASL_FAIL);
 393   }
 394   ret = LOCK_MUTEX(&client_active_mutex);
 395   if (ret < 0) {
 396         UNLOCK_MUTEX(&init_client_mutex);
 397         return (SASL_FAIL);
 398   }
 399   if(gctx->sasl_client_active) {
 400       /* We're already active, just increase our refcount */
 401       /* xxx do something with the callback structure? */
 402       gctx->sasl_client_active++;
 403       UNLOCK_MUTEX(&client_active_mutex);
 404       UNLOCK_MUTEX(&init_client_mutex);
 405       return SASL_OK;
 406   }
 407 
 408   gctx->client_global_callbacks.callbacks = callbacks;
 409   gctx->client_global_callbacks.appname = NULL;
 410 
 411   gctx->cmechlist=sasl_ALLOC(sizeof(cmech_list_t));
 412   if (gctx->cmechlist==NULL) {
 413       UNLOCK_MUTEX(&init_client_mutex);
 414       UNLOCK_MUTEX(&client_active_mutex);
 415       return SASL_NOMEM;
 416   }
 417 
 418   gctx->sasl_client_active = 1;
 419   UNLOCK_MUTEX(&client_active_mutex);
 420 
 421   /* load plugins */
 422   ret=init_mechlist(gctx);
 423 
 424   if (ret!=SASL_OK) {
 425     client_done(gctx);
 426     UNLOCK_MUTEX(&init_client_mutex);
 427     return ret;
 428   }
 429   _sasl_client_add_plugin(gctx, "EXTERNAL", &external_client_plug_init);
 430 
 431   ret = _sasl_common_init(gctx, &gctx->client_global_callbacks, 0);
 432 #else
 433 int sasl_client_init(const sasl_callback_t *callbacks)
 434 {
 435   int ret;
 436   const add_plugin_list_t ep_list[] = {
 437       { "sasl_client_plug_init", (add_plugin_t *)sasl_client_add_plugin },
 438       { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin },
 439       { NULL, NULL }
 440   };
 441 
 442   if(_sasl_client_active) {
 443       /* We're already active, just increase our refcount */
 444       /* xxx do something with the callback structure? */
 445       _sasl_client_active++;
 446       return SASL_OK;
 447   }
 448 
 449   global_callbacks.callbacks = callbacks;
 450   global_callbacks.appname = NULL;
 451 
 452   cmechlist=sasl_ALLOC(sizeof(cmech_list_t));
 453   if (cmechlist==NULL) return SASL_NOMEM;
 454 
 455   /* We need to call client_done if we fail now */
 456   _sasl_client_active = 1;
 457 
 458   /* load plugins */
 459   ret=init_mechlist();  
 460   if (ret!=SASL_OK) {
 461       client_done();
 462       return ret;
 463   }
 464 
 465   sasl_client_add_plugin("EXTERNAL", &external_client_plug_init);
 466 
 467   ret = _sasl_common_init(&global_callbacks);
 468 #endif /* _SUN_SDK_ */
 469 
 470   if (ret == SASL_OK)
 471 #ifdef _SUN_SDK_
 472       ret = _load_client_plugins(gctx);
 473 #else
 474       ret = _sasl_load_plugins(ep_list,
 475                                _sasl_find_getpath_callback(callbacks),
 476                                _sasl_find_verifyfile_callback(callbacks));
 477 #endif /* _SUN_SDK_ */
 478   
 479 #ifdef _SUN_SDK_
 480   if (ret == SASL_OK)
 481         /* If sasl_client_init returns error, sasl_done() need not be called */
 482       ret = _sasl_build_mechlist(gctx);
 483   if (ret == SASL_OK) {
 484       gctx->sasl_client_cleanup_hook = &client_done;
 485       gctx->sasl_client_idle_hook = &client_idle;
 486   } else {
 487       client_done(gctx);
 488   }
 489   UNLOCK_MUTEX(&init_client_mutex);
 490 #else
 491   if (ret == SASL_OK) {
 492       _sasl_client_cleanup_hook = &client_done;
 493       _sasl_client_idle_hook = &client_idle;
 494 
 495       ret = _sasl_build_mechlist();
 496   } else {
 497       client_done();
 498   }
 499 #endif /* _SUN_SDK_ */
 500       
 501   return ret;
 502 }
 503 
 504 static void client_dispose(sasl_conn_t *pconn)
 505 {
 506   sasl_client_conn_t *c_conn=(sasl_client_conn_t *) pconn;
 507 #ifdef _SUN_SDK_
 508   sasl_free_t *free_func = c_conn->cparams->utils->free;
 509 #endif /* _SUN_SDK_ */
 510 
 511   if (c_conn->mech && c_conn->mech->plug->mech_dispose) {
 512     c_conn->mech->plug->mech_dispose(pconn->context,
 513                                      c_conn->cparams->utils);
 514   }
 515 
 516   pconn->context = NULL;
 517 
 518   if (c_conn->clientFQDN)
 519 #ifdef _SUN_SDK_
 520       free_func(c_conn->clientFQDN);
 521 #else
 522       sasl_FREE(c_conn->clientFQDN);
 523 #endif /* _SUN_SDK_ */
 524 
 525   if (c_conn->cparams) {
 526       _sasl_free_utils(&(c_conn->cparams->utils));
 527 #ifdef _SUN_SDK_
 528       free_func(c_conn->cparams);
 529 #else
 530       sasl_FREE(c_conn->cparams);
 531 #endif /* _SUN_SDK_ */
 532   }
 533 
 534   _sasl_conn_dispose(pconn);
 535 }
 536 
 537 /* initialize a client exchange based on the specified mechanism
 538  *  service       -- registered name of the service using SASL (e.g. "imap")
 539  *  serverFQDN    -- the fully qualified domain name of the server
 540  *  iplocalport   -- client IPv4/IPv6 domain literal string with port
 541  *                    (if NULL, then mechanisms requiring IPaddr are disabled)
 542  *  ipremoteport  -- server IPv4/IPv6 domain literal string with port
 543  *                    (if NULL, then mechanisms requiring IPaddr are disabled)
 544  *  prompt_supp   -- list of client interactions supported
 545  *                   may also include sasl_getopt_t context & call
 546  *                   NULL prompt_supp = user/pass via SASL_INTERACT only
 547  *                   NULL proc = interaction supported via SASL_INTERACT
 548  *  secflags      -- security flags (see above)
 549  * in/out:
 550  *  pconn         -- connection negotiation structure
 551  *                   pointer to NULL => allocate new
 552  *                   non-NULL => recycle storage and go for next available mech
 553  *
 554  * Returns:
 555  *  SASL_OK       -- success
 556  *  SASL_NOMECH   -- no mechanism meets requested properties
 557  *  SASL_NOMEM    -- not enough memory
 558  */
 559 int sasl_client_new(const char *service,
 560                     const char *serverFQDN,
 561                     const char *iplocalport,
 562                     const char *ipremoteport,
 563                     const sasl_callback_t *prompt_supp,
 564                     unsigned flags,
 565                     sasl_conn_t **pconn)
 566 {
 567 #ifdef _SUN_SDK_
 568     return _sasl_client_new(NULL, service, serverFQDN, iplocalport,
 569                             ipremoteport, prompt_supp, flags, pconn);
 570 }
 571 int _sasl_client_new(void *ctx,
 572                      const char *service,
 573                      const char *serverFQDN,
 574                      const char *iplocalport,
 575                      const char *ipremoteport,
 576                      const sasl_callback_t *prompt_supp,
 577                      unsigned flags,
 578                      sasl_conn_t **pconn)
 579 {
 580   _sasl_global_context_t *gctx = ctx == NULL ? _sasl_gbl_ctx() : ctx;
 581 #endif /* _SUN_SDK_ */
 582   int result;
 583   char name[MAXHOSTNAMELEN];
 584   sasl_client_conn_t *conn;
 585   sasl_utils_t *utils;
 586 
 587 #ifdef _SUN_SDK_
 588   if (gctx == NULL)
 589         gctx = _sasl_gbl_ctx();
 590 
 591   if(gctx->sasl_client_active==0) return SASL_NOTINIT;
 592 #else
 593   if(_sasl_client_active==0) return SASL_NOTINIT;
 594 #endif /* _SUN_SDK_ */
 595   
 596   /* Remember, iplocalport and ipremoteport can be NULL and be valid! */
 597   if (!pconn || !service || !serverFQDN)
 598     return SASL_BADPARAM;
 599 
 600   *pconn=sasl_ALLOC(sizeof(sasl_client_conn_t));
 601   if (*pconn==NULL) {
 602 #ifdef _SUN_SDK_
 603       __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_ERR,
 604                 "Out of memory allocating connection context");
 605 #else
 606       _sasl_log(NULL, SASL_LOG_ERR,
 607                 "Out of memory allocating connection context");
 608 #endif /* _SUN_SDK_ */
 609       return SASL_NOMEM;
 610   }
 611   memset(*pconn, 0, sizeof(sasl_client_conn_t));
 612 
 613 #ifdef _SUN_SDK_
 614   (*pconn)->gctx = gctx;
 615 #endif /* _SUN_SDK_ */
 616 
 617   (*pconn)->destroy_conn = &client_dispose;
 618 
 619   conn = (sasl_client_conn_t *)*pconn;
 620   
 621   conn->mech = NULL;
 622 
 623   conn->cparams=sasl_ALLOC(sizeof(sasl_client_params_t));
 624   if (conn->cparams==NULL) 
 625       MEMERROR(*pconn);
 626   memset(conn->cparams,0,sizeof(sasl_client_params_t));
 627 
 628   result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_CLIENT,
 629                            &client_idle, serverFQDN,
 630                            iplocalport, ipremoteport,
 631 #ifdef _SUN_SDK_
 632                            prompt_supp, &gctx->client_global_callbacks);
 633 #else
 634                            prompt_supp, &global_callbacks);
 635 #endif /* _SUN_SDK_ */
 636 
 637   if (result != SASL_OK) RETURN(*pconn, result);
 638   
 639 #ifdef _SUN_SDK_
 640   utils=_sasl_alloc_utils(gctx, *pconn, &gctx->client_global_callbacks);
 641 #else
 642   utils=_sasl_alloc_utils(*pconn, &global_callbacks);
 643 #endif /* _SUN_SDK_ */
 644   if (utils==NULL)
 645       MEMERROR(*pconn);
 646   
 647   utils->conn= *pconn;
 648 
 649   /* Setup the non-lazy parts of cparams, the rest is done in
 650    * sasl_client_start */
 651   conn->cparams->utils = utils;
 652   conn->cparams->canon_user = &_sasl_canon_user;
 653   conn->cparams->flags = flags;
 654   conn->cparams->prompt_supp = (*pconn)->callbacks;
 655   
 656   /* get the clientFQDN (serverFQDN was set in _sasl_conn_init) */
 657   memset(name, 0, sizeof(name));
 658   gethostname(name, MAXHOSTNAMELEN);
 659 
 660   result = _sasl_strdup(name, &conn->clientFQDN, NULL);
 661 
 662   if(result == SASL_OK) return SASL_OK;
 663 
 664 #ifdef _SUN_SDK_
 665   conn->cparams->iplocalport = (*pconn)->iplocalport;
 666   conn->cparams->iploclen = strlen((*pconn)->iplocalport);
 667   conn->cparams->ipremoteport = (*pconn)->ipremoteport;
 668   conn->cparams->ipremlen = strlen((*pconn)->ipremoteport);
 669 #endif /* _SUN_SDK_ */
 670 
 671   /* result isn't SASL_OK */
 672   _sasl_conn_dispose(*pconn);
 673   sasl_FREE(*pconn);
 674   *pconn = NULL;
 675 #ifdef _SUN_SDK_
 676   __sasl_log(gctx, gctx->client_global_callbacks.callbacks, SASL_LOG_ERR,
 677         "Out of memory in sasl_client_new");
 678 #else
 679   _sasl_log(NULL, SASL_LOG_ERR, "Out of memory in sasl_client_new");
 680 #endif /* _SUN_SDK_ */
 681   return result;
 682 }
 683 
 684 static int have_prompts(sasl_conn_t *conn,
 685                         const sasl_client_plug_t *mech)
 686 {
 687   static const unsigned long default_prompts[] = {
 688     SASL_CB_AUTHNAME,
 689     SASL_CB_PASS,
 690     SASL_CB_LIST_END
 691   };
 692 
 693   const unsigned long *prompt;
 694   int (*pproc)();
 695   void *pcontext;
 696   int result;
 697 
 698   for (prompt = (mech->required_prompts
 699                  ? mech->required_prompts :
 700                  default_prompts);
 701        *prompt != SASL_CB_LIST_END;
 702        prompt++) {
 703     result = _sasl_getcallback(conn, *prompt, &pproc, &pcontext);
 704     if (result != SASL_OK && result != SASL_INTERACT)
 705       return 0;                 /* we don't have this required prompt */
 706   }
 707 
 708   return 1; /* we have all the prompts */
 709 }
 710 
 711 /* select a mechanism for a connection
 712  *  mechlist      -- mechanisms server has available (punctuation ignored)
 713  *  secret        -- optional secret from previous session
 714  * output:
 715  *  prompt_need   -- on SASL_INTERACT, list of prompts needed to continue
 716  *  clientout     -- the initial client response to send to the server
 717  *  mech          -- set to mechanism name
 718  *
 719  * Returns:
 720  *  SASL_OK       -- success
 721  *  SASL_NOMEM    -- not enough memory
 722  *  SASL_NOMECH   -- no mechanism meets requested properties
 723  *  SASL_INTERACT -- user interaction needed to fill in prompt_need list
 724  */
 725 
 726 /* xxx confirm this with rfc 2222
 727  * SASL mechanism allowable characters are "AZaz-_"
 728  * seperators can be any other characters and of any length
 729  * even variable lengths between
 730  *
 731  * Apps should be encouraged to simply use space or comma space
 732  * though
 733  */
 734 int sasl_client_start(sasl_conn_t *conn,
 735                       const char *mechlist,
 736                       sasl_interact_t **prompt_need,
 737                       const char **clientout,
 738                       unsigned *clientoutlen,
 739                       const char **mech)
 740 {
 741     sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
 742     char name[SASL_MECHNAMEMAX + 1];
 743     cmechanism_t *m=NULL,*bestm=NULL;
 744     size_t pos=0,place;
 745     size_t list_len;
 746     sasl_ssf_t bestssf = 0, minssf = 0;
 747     int result;
 748 #ifdef _SUN_SDK_
 749     _sasl_global_context_t *gctx = (conn == NULL) ?
 750                 _sasl_gbl_ctx() : conn->gctx;
 751     cmech_list_t *cmechlist;
 752 
 753     if(gctx->sasl_client_active==0) return SASL_NOTINIT;
 754     cmechlist = gctx->cmechlist;
 755 #else
 756     if(_sasl_client_active==0) return SASL_NOTINIT;
 757 #endif /* _SUN_SDK_ */
 758 
 759     if (!conn) return SASL_BADPARAM;
 760 
 761     /* verify parameters */
 762     if (mechlist == NULL)
 763         PARAMERROR(conn);
 764 
 765     /* if prompt_need != NULL we've already been here
 766        and just need to do the continue step again */
 767 
 768     /* do a step */
 769     /* FIXME: Hopefully they only give us our own prompt_need back */
 770     if (prompt_need && *prompt_need != NULL) {
 771         goto dostep;
 772     }
 773 
 774 #ifdef _SUN_SDK_
 775     if (c_conn->mech != NULL) {
 776         if (c_conn->mech->plug->mech_dispose != NULL) {
 777             c_conn->mech->plug->mech_dispose(conn->context,
 778                 c_conn->cparams->utils);
 779             c_conn->mech = NULL;
 780         }
 781     }
 782     memset(&conn->oparams, 0, sizeof(sasl_out_params_t));
 783 
 784     (void) _load_client_plugins(gctx);
 785 #endif /* _SUN_SDK_ */
 786 
 787     if(conn->props.min_ssf < conn->external.ssf) {
 788         minssf = 0;
 789     } else {
 790         minssf = conn->props.min_ssf - conn->external.ssf;
 791     }
 792 
 793     /* parse mechlist */
 794     list_len = strlen(mechlist);
 795 
 796     while (pos<list_len)
 797     {
 798         place=0;
 799         while ((pos<list_len) && (isalnum((unsigned char)mechlist[pos])
 800                                   || mechlist[pos] == '_'
 801                                   || mechlist[pos] == '-')) {
 802             name[place]=mechlist[pos];
 803             pos++;
 804             place++;
 805             if (SASL_MECHNAMEMAX < place) {
 806                 place--;
 807                 while(pos<list_len && (isalnum((unsigned char)mechlist[pos])
 808                                        || mechlist[pos] == '_'
 809                                        || mechlist[pos] == '-'))
 810                     pos++;
 811             }
 812         }
 813         pos++;
 814         name[place]=0;
 815 
 816         if (! place) continue;
 817 
 818         /* foreach in server list */
 819         for (m = cmechlist->mech_list; m != NULL; m = m->next) {
 820             int myflags;
 821             
 822             /* Is this the mechanism the server is suggesting? */
 823             if (strcasecmp(m->plug->mech_name, name))
 824                 continue; /* no */
 825 
 826             /* Do we have the prompts for it? */
 827             if (!have_prompts(conn, m->plug))
 828                 break;
 829 
 830             /* Is it strong enough? */
 831             if (minssf > m->plug->max_ssf)
 832                 break;
 833 
 834 #ifdef _INTEGRATED_SOLARIS_
 835             /* If not SUN supplied mech, it has no strength */
 836             if (minssf > 0 && !m->sun_reg)
 837                 break;
 838 #endif /* _INTEGRATED_SOLARIS_ */
 839 
 840             /* Does it meet our security properties? */
 841             myflags = conn->props.security_flags;
 842             
 843             /* if there's an external layer this is no longer plaintext */
 844             if ((conn->props.min_ssf <= conn->external.ssf) && 
 845                 (conn->external.ssf > 1)) {
 846                 myflags &= ~SASL_SEC_NOPLAINTEXT;
 847             }
 848 
 849             if (((myflags ^ m->plug->security_flags) & myflags) != 0) {
 850                 break;
 851             }
 852 
 853             /* Can we meet it's features? */
 854             if ((m->plug->features & SASL_FEAT_NEEDSERVERFQDN)
 855                 && !conn->serverFQDN) {
 856                 break;
 857             }
 858 
 859             /* Can it meet our features? */
 860             if ((conn->flags & SASL_NEED_PROXY) &&
 861                 !(m->plug->features & SASL_FEAT_ALLOWS_PROXY)) {
 862                 break;
 863             }
 864             
 865 #ifdef PREFER_MECH
 866 #ifdef _INTEGRATED_SOLARIS_
 867             if (strcasecmp(m->plug->mech_name, PREFER_MECH) &&
 868                 bestm && (m->sun_reg && m->plug->max_ssf <= bestssf) ||
 869                 (m->plug->max_ssf == 0)) {
 870 #else
 871             if (strcasecmp(m->plug->mech_name, PREFER_MECH) &&
 872                 bestm && m->plug->max_ssf <= bestssf) {
 873 #endif /* _INTEGRATED_SOLARIS_ */
 874 
 875                 /* this mechanism isn't our favorite, and it's no better
 876                    than what we already have! */
 877                 break;
 878             }
 879 #else
 880 #ifdef _INTEGRATED_SOLARIS_
 881             if (bestm && m->sun_reg && m->plug->max_ssf <= bestssf) {
 882 #else
 883 
 884             if (bestm && m->plug->max_ssf <= bestssf) {
 885 #endif /* _INTEGRATED_SOLARIS_ */
 886 
 887                 /* this mechanism is no better than what we already have! */
 888                 break;
 889             }
 890 #endif
 891 
 892             /* compare security flags, only take new mechanism if it has
 893              * all the security flags of the previous one.
 894              *
 895              * From the mechanisms we ship with, this yields the order:
 896              *
 897              * SRP
 898              * GSSAPI + KERBEROS_V4
 899              * DIGEST + OTP
 900              * CRAM + EXTERNAL
 901              * PLAIN + LOGIN + ANONYMOUS
 902              *
 903              * This might be improved on by comparing the numeric value of
 904              * the bitwise-or'd security flags, which splits DIGEST/OTP,
 905              * CRAM/EXTERNAL, and PLAIN/LOGIN from ANONYMOUS, but then we
 906              * are depending on the numeric values of the flags (which may
 907              * change, and their ordering could be considered dumb luck.
 908              */
 909 
 910             if (bestm &&
 911                 ((m->plug->security_flags ^ bestm->plug->security_flags) &
 912                  bestm->plug->security_flags)) {
 913                 break;
 914             }
 915 
 916             if (mech) {
 917                 *mech = m->plug->mech_name;
 918             }
 919 #ifdef _INTEGRATED_SOLARIS_
 920             bestssf = m->sun_reg ? m->plug->max_ssf : 0;
 921 #else
 922             bestssf = m->plug->max_ssf;
 923 #endif /* _INTEGRATED_SOLARIS_ */
 924             bestm = m;
 925             break;
 926         }
 927     }
 928 
 929     if (bestm == NULL) {
 930 #ifdef _INTEGRATED_SOLARIS_
 931         sasl_seterror(conn, 0, gettext("No worthy mechs found"));
 932 #else
 933         sasl_seterror(conn, 0, "No worthy mechs found");
 934 #endif /* _INTEGRATED_SOLARIS_ */
 935         result = SASL_NOMECH;
 936         goto done;
 937     }
 938 
 939     /* make (the rest of) cparams */
 940     c_conn->cparams->service = conn->service;
 941     c_conn->cparams->servicelen = strlen(conn->service);
 942     
 943     c_conn->cparams->serverFQDN = conn->serverFQDN; 
 944     c_conn->cparams->slen = strlen(conn->serverFQDN);
 945 
 946     c_conn->cparams->clientFQDN = c_conn->clientFQDN; 
 947     c_conn->cparams->clen = strlen(c_conn->clientFQDN);
 948 
 949     c_conn->cparams->external_ssf = conn->external.ssf;
 950     c_conn->cparams->props = conn->props;
 951 #ifdef _INTEGRATED_SOLARIS_
 952     if (!bestm->sun_reg) {
 953         c_conn->cparams->props.min_ssf = 0;
 954         c_conn->cparams->props.max_ssf = 0;
 955     }
 956     c_conn->base.sun_reg = bestm->sun_reg;
 957 #endif /* _INTEGRATED_SOLARIS_ */
 958     c_conn->mech = bestm;
 959 
 960     /* init that plugin */
 961 #ifdef _SUN_SDK_
 962     result = c_conn->mech->plug->mech_new(c_conn->mech->glob_context,
 963 #else
 964     result = c_conn->mech->plug->mech_new(c_conn->mech->plug->glob_context,
 965 #endif /* _SUN_SDK_ */
 966                                           c_conn->cparams,
 967                                           &(conn->context));
 968     if(result != SASL_OK) goto done;
 969 
 970     /* do a step -- but only if we can do a client-send-first */
 971  dostep:
 972     if(clientout) {
 973         if(c_conn->mech->plug->features & SASL_FEAT_SERVER_FIRST) {
 974             *clientout = NULL;
 975             *clientoutlen = 0;
 976             result = SASL_CONTINUE;
 977         } else {
 978             result = sasl_client_step(conn, NULL, 0, prompt_need,
 979                                       clientout, clientoutlen);
 980         }
 981     }
 982     else
 983         result = SASL_CONTINUE;
 984 
 985  done:
 986     RETURN(conn, result);
 987 }
 988 
 989 /* do a single authentication step.
 990  *  serverin    -- the server message received by the client, MUST have a NUL
 991  *                 sentinel, not counted by serverinlen
 992  * output:
 993  *  prompt_need -- on SASL_INTERACT, list of prompts needed to continue
 994  *  clientout   -- the client response to send to the server
 995  *
 996  * returns:
 997  *  SASL_OK        -- success
 998  *  SASL_INTERACT  -- user interaction needed to fill in prompt_need list
 999  *  SASL_BADPROT   -- server protocol incorrect/cancelled
1000  *  SASL_BADSERV   -- server failed mutual auth
1001  */
1002 
1003 int sasl_client_step(sasl_conn_t *conn,
1004                      const char *serverin,
1005                      unsigned serverinlen,
1006                      sasl_interact_t **prompt_need,
1007                      const char **clientout,
1008                      unsigned *clientoutlen)
1009 {
1010   sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
1011   int result;
1012 
1013 #ifdef _SUN_SDK_
1014   _sasl_global_context_t *gctx = (conn == NULL) ?
1015                 _sasl_gbl_ctx() : conn->gctx;
1016 
1017   if(gctx->sasl_client_active==0) return SASL_NOTINIT;
1018 #else
1019   if(_sasl_client_active==0) return SASL_NOTINIT;
1020 #endif  /* _SUN_SDK_ */
1021   if(!conn) return SASL_BADPARAM;
1022 
1023   /* check parameters */
1024   if ((serverin==NULL) && (serverinlen>0))
1025       PARAMERROR(conn);
1026 
1027   /* Don't do another step if the plugin told us that we're done */
1028   if (conn->oparams.doneflag) {
1029       _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag");
1030       return SASL_FAIL;
1031   }
1032 
1033   if(clientout) *clientout = NULL;
1034   if(clientoutlen) *clientoutlen = 0;
1035 
1036   /* do a step */
1037   result = c_conn->mech->plug->mech_step(conn->context,
1038                                          c_conn->cparams,
1039                                          serverin,
1040                                          serverinlen,
1041                                          prompt_need,
1042                                          clientout, clientoutlen,
1043                                          &conn->oparams);
1044 
1045   if (result == SASL_OK) {
1046       /* So we're done on this end, but if both
1047        * 1. the mech does server-send-last
1048        * 2. the protocol does not
1049        * we need to return no data */
1050       if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) {
1051           *clientout = "";
1052           *clientoutlen = 0;
1053       }
1054       
1055       if(!conn->oparams.maxoutbuf) {
1056           conn->oparams.maxoutbuf = conn->props.maxbufsize;
1057       }
1058 
1059       if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
1060 #ifdef _SUN_SDK_
1061         _sasl_log(conn, SASL_LOG_ERR,
1062                   "mech did not call canon_user for both authzid and authid");
1063 #else
1064           sasl_seterror(conn, 0,
1065                         "mech did not call canon_user for both authzid and authid");
1066 #endif /* _SUN_SDK_ */
1067           result = SASL_BADPROT;
1068       }
1069   }  
1070 
1071   RETURN(conn,result);
1072 }
1073 
1074 /* returns the length of all the mechanisms
1075  * added up 
1076  */
1077 
1078 #ifdef _SUN_SDK_
1079 static unsigned mech_names_len(_sasl_global_context_t *gctx)
1080 {
1081   cmech_list_t *cmechlist = gctx->cmechlist;
1082 #else
1083 static unsigned mech_names_len()
1084 {
1085 #endif /* _SUN_SDK_ */
1086   cmechanism_t *listptr;
1087   unsigned result = 0;
1088 
1089   for (listptr = cmechlist->mech_list;
1090        listptr;
1091        listptr = listptr->next)
1092     result += strlen(listptr->plug->mech_name);
1093 
1094   return result;
1095 }
1096 
1097 
1098 int _sasl_client_listmech(sasl_conn_t *conn,
1099                           const char *prefix,
1100                           const char *sep,
1101                           const char *suffix,
1102                           const char **result,
1103                           unsigned *plen,
1104                           int *pcount)
1105 {
1106     cmechanism_t *m=NULL;
1107     sasl_ssf_t minssf = 0;
1108     int ret;
1109     unsigned int resultlen;
1110     int flag;
1111     const char *mysep;
1112 #ifdef _SUN_SDK_
1113     _sasl_global_context_t *gctx = conn == NULL ? _sasl_gbl_ctx() : conn->gctx;
1114     cmech_list_t *cmechlist;
1115 
1116     if(gctx->sasl_client_active==0) return SASL_NOTINIT;
1117     cmechlist = gctx->cmechlist;
1118 #else
1119     if(_sasl_client_active == 0) return SASL_NOTINIT;
1120 #endif /* _SUN_SDK_ */
1121     if (!conn) return SASL_BADPARAM;
1122     if(conn->type != SASL_CONN_CLIENT) PARAMERROR(conn);
1123     
1124     if (! result)
1125         PARAMERROR(conn);
1126     
1127 #ifdef _SUN_SDK_
1128      (void) _load_client_plugins(gctx);
1129 #endif /* _SUN_SDK_ */
1130 
1131     if (plen != NULL)
1132         *plen = 0;
1133     if (pcount != NULL)
1134         *pcount = 0;
1135 
1136     if (sep) {
1137         mysep = sep;
1138     } else {
1139         mysep = " ";
1140     }
1141 
1142     if(conn->props.min_ssf < conn->external.ssf) {
1143         minssf = 0;
1144     } else {
1145         minssf = conn->props.min_ssf - conn->external.ssf;
1146     }
1147 
1148     if (! cmechlist || cmechlist->mech_length <= 0)
1149         INTERROR(conn, SASL_NOMECH);
1150 
1151     resultlen = (prefix ? strlen(prefix) : 0)
1152         + (strlen(mysep) * (cmechlist->mech_length - 1))
1153 #ifdef _SUN_SDK_
1154         + mech_names_len(gctx)
1155 #else
1156         + mech_names_len()
1157 #endif /* _SUN_SDK_ */
1158         + (suffix ? strlen(suffix) : 0)
1159         + 1;
1160     ret = _buf_alloc(&conn->mechlist_buf,
1161                      &conn->mechlist_buf_len, resultlen);
1162     if(ret != SASL_OK) MEMERROR(conn);
1163 
1164     if (prefix)
1165         strcpy (conn->mechlist_buf,prefix);
1166     else
1167         *(conn->mechlist_buf) = '\0';
1168 
1169     flag = 0;
1170     for (m = cmechlist->mech_list; m != NULL; m = m->next) {
1171             /* do we have the prompts for it? */
1172             if (!have_prompts(conn, m->plug))
1173                 continue;
1174 
1175             /* is it strong enough? */
1176             if (minssf > m->plug->max_ssf)
1177                 continue;
1178 
1179 #ifdef _INTEGRATED_SOLARIS_
1180             /* If not SUN supplied mech, it has no strength */
1181             if (minssf > 0 && !m->sun_reg)
1182                 continue;
1183 #endif /* _INTEGRATED_SOLARIS_ */
1184 
1185             /* does it meet our security properties? */
1186             if (((conn->props.security_flags ^ m->plug->security_flags)
1187                  & conn->props.security_flags) != 0) {
1188                 continue;
1189             }
1190 
1191             /* Can we meet it's features? */
1192             if ((m->plug->features & SASL_FEAT_NEEDSERVERFQDN)
1193                 && !conn->serverFQDN) {
1194                 continue;
1195             }
1196 
1197             /* Can it meet our features? */
1198             if ((conn->flags & SASL_NEED_PROXY) &&
1199                 !(m->plug->features & SASL_FEAT_ALLOWS_PROXY)) {
1200                 break;
1201             }
1202 
1203             /* Okay, we like it, add it to the list! */
1204 
1205             if (pcount != NULL)
1206                 (*pcount)++;
1207 
1208             /* print seperator */
1209             if (flag) {
1210                 strcat(conn->mechlist_buf, mysep);
1211             } else {
1212                 flag = 1;
1213             }
1214             
1215             /* now print the mechanism name */
1216             strcat(conn->mechlist_buf, m->plug->mech_name);
1217     }
1218     
1219   if (suffix)
1220       strcat(conn->mechlist_buf,suffix);
1221 
1222   if (plen!=NULL)
1223       *plen=strlen(conn->mechlist_buf);
1224 
1225   *result = conn->mechlist_buf;
1226 
1227   return SASL_OK;
1228 }
1229 
1230 #ifdef _SUN_SDK_
1231 sasl_string_list_t *_sasl_client_mechs(_sasl_global_context_t *gctx)
1232 {
1233   cmech_list_t *cmechlist = gctx->cmechlist;
1234 #else
1235 sasl_string_list_t *_sasl_client_mechs(void) 
1236 {
1237 #endif /* _SUN_SDK_ */
1238   cmechanism_t *listptr;
1239   sasl_string_list_t *retval = NULL, *next=NULL;
1240 
1241 #ifdef _SUN_SDK_
1242   if(!gctx->sasl_client_active) return NULL;
1243 #else
1244   if(!_sasl_client_active) return NULL;
1245 #endif /* _SUN_SDK_ */
1246 
1247   /* make list */
1248   for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) {
1249       next = sasl_ALLOC(sizeof(sasl_string_list_t));
1250 
1251       if(!next && !retval) return NULL;
1252       else if(!next) {
1253           next = retval->next;
1254           do {
1255               sasl_FREE(retval);
1256               retval = next;
1257               next = retval->next;
1258           } while(next);
1259           return NULL;
1260       }
1261       
1262       next->d = listptr->plug->mech_name;
1263 
1264       if(!retval) {
1265           next->next = NULL;
1266           retval = next;
1267       } else {
1268           next->next = retval;
1269           retval = next;
1270       }
1271   }
1272 
1273   return retval;
1274 }