imapext-2007

diff src/c-client/auth_gss.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/c-client/auth_gss.c	Mon Sep 14 15:17:45 2009 +0900
     1.3 @@ -0,0 +1,423 @@
     1.4 +/* ========================================================================
     1.5 + * Copyright 1988-2006 University of Washington
     1.6 + *
     1.7 + * Licensed under the Apache License, Version 2.0 (the "License");
     1.8 + * you may not use this file except in compliance with the License.
     1.9 + * You may obtain a copy of the License at
    1.10 + *
    1.11 + *     http://www.apache.org/licenses/LICENSE-2.0
    1.12 + *
    1.13 + * 
    1.14 + * ========================================================================
    1.15 + */
    1.16 +
    1.17 +/*
    1.18 + * Program:	GSSAPI authenticator
    1.19 + *
    1.20 + * Author:	Mark Crispin
    1.21 + *		Networks and Distributed Computing
    1.22 + *		Computing & Communications
    1.23 + *		University of Washington
    1.24 + *		Administration Building, AG-44
    1.25 + *		Seattle, WA  98195
    1.26 + *		Internet: MRC@CAC.Washington.EDU
    1.27 + *
    1.28 + * Date:	12 January 1998
    1.29 + * Last Edited:	30 August 2006
    1.30 + */
    1.31 +
    1.32 +
    1.33 +long auth_gssapi_valid (void);
    1.34 +long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder,
    1.35 +			 char *service,NETMBX *mb,void *stream,
    1.36 +			 unsigned long *trial,char *user);
    1.37 +long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal,
    1.38 +			      authrespond_t responder,char *service,NETMBX *mb,
    1.39 +			      void *stream,char *user,kinit_t ki);
    1.40 +char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[]);
    1.41 +
    1.42 +
    1.43 +AUTHENTICATOR auth_gss = {
    1.44 +  AU_SECURE | AU_AUTHUSER,	/* secure authenticator */
    1.45 +  "GSSAPI",			/* authenticator name */
    1.46 +  auth_gssapi_valid,		/* check if valid */
    1.47 +  auth_gssapi_client,		/* client method */
    1.48 +  auth_gssapi_server,		/* server method */
    1.49 +  NIL				/* next authenticator */
    1.50 +};
    1.51 +
    1.52 +#define AUTH_GSSAPI_P_NONE 1
    1.53 +#define AUTH_GSSAPI_P_INTEGRITY 2
    1.54 +#define AUTH_GSSAPI_P_PRIVACY 4
    1.55 +
    1.56 +#define AUTH_GSSAPI_C_MAXSIZE 8192
    1.57 +
    1.58 +#define SERVER_LOG(x,y) syslog (LOG_ALERT,x,y)
    1.59 +
    1.60 +/* Check if GSSAPI valid on this system
    1.61 + * Returns: T if valid, NIL otherwise
    1.62 + */
    1.63 +
    1.64 +long auth_gssapi_valid (void)
    1.65 +{
    1.66 +  char tmp[MAILTMPLEN];
    1.67 +  OM_uint32 smn;
    1.68 +  gss_buffer_desc buf;
    1.69 +  gss_name_t name;
    1.70 +				/* make service name */
    1.71 +  sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
    1.72 +	   mylocalhost ());
    1.73 +  buf.length = strlen (buf.value = tmp);
    1.74 +				/* see if can build a name */
    1.75 +  if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&name) !=
    1.76 +      GSS_S_COMPLETE) return NIL;
    1.77 +				/* remove server method if no keytab */
    1.78 +  if (!kerberos_server_valid ()) auth_gss.server = NIL;
    1.79 +  gss_release_name (&smn,&name);/* finished with name */
    1.80 +  return LONGT;
    1.81 +}
    1.82 +
    1.83 +/* Client authenticator
    1.84 + * Accepts: challenger function
    1.85 + *	    responder function
    1.86 + *	    SASL service name
    1.87 + *	    parsed network mailbox structure
    1.88 + *	    stream argument for functions
    1.89 + *	    pointer to current trial count
    1.90 + *	    returned user name
    1.91 + * Returns: T if success, NIL otherwise, number of trials incremented if retry
    1.92 + */
    1.93 +
    1.94 +long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder,
    1.95 +			 char *service,NETMBX *mb,void *stream,
    1.96 +			 unsigned long *trial,char *user)
    1.97 +{
    1.98 +  gss_buffer_desc chal;
    1.99 +  kinit_t ki = (kinit_t) mail_parameters (NIL,GET_KINIT,NIL);
   1.100 +  long ret = NIL;
   1.101 +  *trial = 65535;		/* never retry */
   1.102 +				/* get initial (empty) challenge */
   1.103 +  if (chal.value = (*challenger) (stream,(unsigned long *) &chal.length)) {
   1.104 +    if (chal.length) {		/* abort if challenge non-empty */
   1.105 +      mm_log ("Server bug: non-empty initial GSSAPI challenge",WARN);
   1.106 +      (*responder) (stream,NIL,0);
   1.107 +      ret = LONGT;		/* will get a BAD response back */
   1.108 +    }
   1.109 +    else if (mb->authuser[0] && strcmp (mb->authuser,myusername ())) {
   1.110 +      mm_log ("Can't use Kerberos: invalid /authuser",WARN);
   1.111 +      (*responder) (stream,NIL,0);
   1.112 +      ret = LONGT;		/* will get a BAD response back */
   1.113 +    }
   1.114 +    else ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
   1.115 +					stream,user,ki);
   1.116 +  }
   1.117 +  return ret;
   1.118 +}
   1.119 +
   1.120 +/* Client authenticator worker function
   1.121 + * Accepts: challenger function
   1.122 + *	    responder function
   1.123 + *	    SASL service name
   1.124 + *	    parsed network mailbox structure
   1.125 + *	    stream argument for functions
   1.126 + *	    returned user name
   1.127 + *	    kinit function pointer if should retry with kinit
   1.128 + * Returns: T if success, NIL otherwise
   1.129 + */
   1.130 +
   1.131 +long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal,
   1.132 +			      authrespond_t responder,char *service,NETMBX *mb,
   1.133 +			      void *stream,char *user,kinit_t ki)
   1.134 +{
   1.135 +  char tmp[MAILTMPLEN];
   1.136 +  OM_uint32 smj,smn,dsmj,dsmn;
   1.137 +  OM_uint32 mctx = 0;
   1.138 +  gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
   1.139 +  gss_buffer_desc resp,buf;
   1.140 +  long i;
   1.141 +  int conf;
   1.142 +  gss_qop_t qop;
   1.143 +  gss_name_t crname = NIL;
   1.144 +  blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
   1.145 +  void *data;
   1.146 +  long ret = NIL;
   1.147 +  sprintf (tmp,"%s@%s",service,mb->host);
   1.148 +  buf.length = strlen (buf.value = tmp);
   1.149 +				/* get service name */
   1.150 +  if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname) !=
   1.151 +       GSS_S_COMPLETE) {
   1.152 +    mm_log ("Can't import Kerberos service name",WARN);
   1.153 +    (*responder) (stream,NIL,0);
   1.154 +  }
   1.155 +  else {
   1.156 +    data = (*bn) (BLOCK_SENSITIVE,NIL);
   1.157 +				/* negotiate with KDC */
   1.158 +    smj = gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,crname,NIL,
   1.159 +				GSS_C_INTEG_FLAG | GSS_C_MUTUAL_FLAG |
   1.160 +				GSS_C_REPLAY_FLAG,0,GSS_C_NO_CHANNEL_BINDINGS,
   1.161 +				GSS_C_NO_BUFFER,NIL,&resp,NIL,NIL);
   1.162 +    (*bn) (BLOCK_NONSENSITIVE,data);
   1.163 +
   1.164 +				/* while continuation needed */
   1.165 +    while (smj == GSS_S_CONTINUE_NEEDED) {
   1.166 +      if (chal.value) fs_give ((void **) &chal.value);
   1.167 +				/* send response, get next challenge */
   1.168 +      i = (*responder) (stream,resp.value,resp.length) &&
   1.169 +	(chal.value = (*challenger) (stream,(unsigned long *) &chal.length));
   1.170 +      gss_release_buffer (&smn,&resp);
   1.171 +      if (i) {			/* negotiate continuation with KDC */
   1.172 +	data = (*bn) (BLOCK_SENSITIVE,NIL);
   1.173 +	switch (smj =		/* make sure continuation going OK */
   1.174 +		gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,
   1.175 +				      crname,GSS_C_NO_OID,GSS_C_INTEG_FLAG |
   1.176 +				      GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,0,
   1.177 +				      GSS_C_NO_CHANNEL_BINDINGS,&chal,NIL,
   1.178 +				      &resp,NIL,NIL)) {
   1.179 +	case GSS_S_CONTINUE_NEEDED:
   1.180 +	case GSS_S_COMPLETE:
   1.181 +	  break;
   1.182 +	default:		/* error, don't need context any more */
   1.183 +	  gss_delete_sec_context (&smn,&ctx,NIL);
   1.184 +	}
   1.185 +	(*bn) (BLOCK_NONSENSITIVE,data);
   1.186 +      }
   1.187 +      else {			/* error in continuation */
   1.188 +	mm_log ("Error in negotiating Kerberos continuation",WARN);
   1.189 +	(*responder) (stream,NIL,0);
   1.190 +				/* don't need context any more */
   1.191 +	gss_delete_sec_context (&smn,&ctx,NIL);
   1.192 +	break;
   1.193 +      }
   1.194 +    }
   1.195 +
   1.196 +    switch (smj) {		/* done - deal with final condition */
   1.197 +    case GSS_S_COMPLETE:
   1.198 +      if (chal.value) fs_give ((void **) &chal.value);
   1.199 +				/* get prot mechanisms and max size */
   1.200 +      if ((*responder) (stream,resp.value ? resp.value : "",resp.length) &&
   1.201 +	  (chal.value = (*challenger) (stream,(unsigned long *)&chal.length))&&
   1.202 +	  (gss_unwrap (&smn,ctx,&chal,&resp,&conf,&qop) == GSS_S_COMPLETE) &&
   1.203 +	  (resp.length >= 4) && (*((char *) resp.value) & AUTH_GSSAPI_P_NONE)){
   1.204 +				/* make copy of flags and length */
   1.205 +	memcpy (tmp,resp.value,4);
   1.206 +	gss_release_buffer (&smn,&resp);
   1.207 +				/* no session protection */
   1.208 +	tmp[0] = AUTH_GSSAPI_P_NONE;
   1.209 +				/* install user name */
   1.210 +	strcpy (tmp+4,strcpy (user,mb->user[0] ? mb->user : myusername ()));
   1.211 +	buf.value = tmp; buf.length = strlen (user) + 4;
   1.212 +				/* successful negotiation */
   1.213 +	switch (smj = gss_wrap (&smn,ctx,NIL,qop,&buf,&conf,&resp)) {
   1.214 +	case GSS_S_COMPLETE:
   1.215 +	  if ((*responder) (stream,resp.value,resp.length)) ret = T;
   1.216 +	  gss_release_buffer (&smn,&resp);
   1.217 +	  break;
   1.218 +	default:
   1.219 +	  do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
   1.220 +						GSS_C_NO_OID,&mctx,&resp)) {
   1.221 +	  case GSS_S_COMPLETE:
   1.222 +	    mctx = 0;
   1.223 +	  case GSS_S_CONTINUE_NEEDED:
   1.224 +	    sprintf (tmp,"Unknown gss_wrap failure: %s",(char *) resp.value);
   1.225 +	    mm_log (tmp,WARN);
   1.226 +	    gss_release_buffer (&dsmn,&resp);
   1.227 +	  }
   1.228 +	  while (dsmj == GSS_S_CONTINUE_NEEDED);
   1.229 +	  do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
   1.230 +						GSS_C_NO_OID,&mctx,&resp)) {
   1.231 +	  case GSS_S_COMPLETE:
   1.232 +	  case GSS_S_CONTINUE_NEEDED:
   1.233 +	    sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value);
   1.234 +	    mm_log (tmp,WARN);
   1.235 +	    gss_release_buffer (&dsmn,&resp);
   1.236 +	  }
   1.237 +	  while (dsmj == GSS_S_CONTINUE_NEEDED);
   1.238 +	  (*responder) (stream,NIL,0);
   1.239 +	}
   1.240 +      }
   1.241 +				/* flush final challenge */
   1.242 +      if (chal.value) fs_give ((void **) &chal.value);
   1.243 +				/* don't need context any more */
   1.244 +      gss_delete_sec_context (&smn,&ctx,NIL);
   1.245 +      break;
   1.246 +
   1.247 +    case GSS_S_CREDENTIALS_EXPIRED:
   1.248 +      if (chal.value) fs_give ((void **) &chal.value);
   1.249 +				/* retry if application kinits */
   1.250 +      if (ki && (*ki) (mb->host,"Kerberos credentials expired"))
   1.251 +	ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
   1.252 +				       stream,user,NIL);
   1.253 +      else {			/* application can't kinit */
   1.254 +	sprintf (tmp,"Kerberos credentials expired (try running kinit) for %s",
   1.255 +		 mb->host);
   1.256 +	mm_log (tmp,WARN);
   1.257 +	(*responder) (stream,NIL,0);
   1.258 +      }
   1.259 +      break;
   1.260 +    case GSS_S_FAILURE:
   1.261 +      if (chal.value) fs_give ((void **) &chal.value);
   1.262 +      do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
   1.263 +					    GSS_C_NO_OID,&mctx,&resp)) {
   1.264 +      case GSS_S_COMPLETE:	/* end of message, can kinit? */
   1.265 +	if (ki && kerberos_try_kinit (smn) &&
   1.266 +	    (*ki) (mb->host,(char *) resp.value)) {
   1.267 +	  gss_release_buffer (&dsmn,&resp);
   1.268 +	  ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
   1.269 +					 stream,user,NIL);
   1.270 +	  break;		/* done */
   1.271 +	}
   1.272 +	else (*responder) (stream,NIL,0);
   1.273 +      case GSS_S_CONTINUE_NEEDED:
   1.274 +	sprintf (tmp,kerberos_try_kinit (smn) ?
   1.275 +		 "Kerberos error: %.80s (try running kinit) for %.80s" :
   1.276 +		 "GSSAPI failure: %s for %.80s",(char *) resp.value,mb->host);
   1.277 +	mm_log (tmp,WARN);
   1.278 +	gss_release_buffer (&dsmn,&resp);
   1.279 +      } while (dsmj == GSS_S_CONTINUE_NEEDED);
   1.280 +      break;
   1.281 +
   1.282 +    default:			/* miscellaneous errors */
   1.283 +      if (chal.value) fs_give ((void **) &chal.value);
   1.284 +      do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
   1.285 +					    GSS_C_NO_OID,&mctx,&resp)) {
   1.286 +      case GSS_S_COMPLETE:
   1.287 +	mctx = 0;
   1.288 +      case GSS_S_CONTINUE_NEEDED:
   1.289 +	sprintf (tmp,"Unknown GSSAPI failure: %s",(char *) resp.value);
   1.290 +	mm_log (tmp,WARN);
   1.291 +	gss_release_buffer (&dsmn,&resp);
   1.292 +      }
   1.293 +      while (dsmj == GSS_S_CONTINUE_NEEDED);
   1.294 +      do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
   1.295 +					    GSS_C_NO_OID,&mctx,&resp)) {
   1.296 +      case GSS_S_COMPLETE:
   1.297 +      case GSS_S_CONTINUE_NEEDED:
   1.298 +	sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value);
   1.299 +	mm_log (tmp,WARN);
   1.300 +	gss_release_buffer (&dsmn,&resp);
   1.301 +      }
   1.302 +      while (dsmj == GSS_S_CONTINUE_NEEDED);
   1.303 +      (*responder) (stream,NIL,0);
   1.304 +      break;
   1.305 +    }
   1.306 +				/* finished with credentials name */
   1.307 +    if (crname) gss_release_name (&smn,&crname);
   1.308 +  }
   1.309 +  return ret;			/* return status */
   1.310 +}
   1.311 +
   1.312 +/* Server authenticator
   1.313 + * Accepts: responder function
   1.314 + *	    argument count
   1.315 + *	    argument vector
   1.316 + * Returns: authenticated user name or NIL
   1.317 + */
   1.318 +
   1.319 +char *auth_gssapi_server (authresponse_t responder,int argc,char *argv[])
   1.320 +{
   1.321 +  char *ret = NIL;
   1.322 +  char tmp[MAILTMPLEN];
   1.323 +  unsigned long maxsize = htonl (AUTH_GSSAPI_C_MAXSIZE);
   1.324 +  int conf;
   1.325 +  OM_uint32 smj,smn,dsmj,dsmn,flags;
   1.326 +  OM_uint32 mctx = 0;
   1.327 +  gss_name_t crname,name;
   1.328 +  gss_OID mech;
   1.329 +  gss_buffer_desc chal,resp,buf;
   1.330 +  gss_cred_id_t crd;
   1.331 +  gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
   1.332 +  gss_qop_t qop = GSS_C_QOP_DEFAULT;
   1.333 +				/* make service name */
   1.334 +  sprintf (tmp,"%s@%s",(char *) mail_parameters (NIL,GET_SERVICENAME,NIL),
   1.335 +	   tcp_serverhost ());
   1.336 +  buf.length = strlen (buf.value = tmp);
   1.337 +				/* acquire credentials */
   1.338 +  if ((gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname)) ==
   1.339 +      GSS_S_COMPLETE) {
   1.340 +    if ((smj = gss_acquire_cred (&smn,crname,0,NIL,GSS_C_ACCEPT,&crd,NIL,NIL))
   1.341 +	== GSS_S_COMPLETE) {
   1.342 +      if (resp.value = (*responder) ("",0,(unsigned long *) &resp.length)) {
   1.343 +	do {			/* negotiate authentication */
   1.344 +	  smj = gss_accept_sec_context (&smn,&ctx,crd,&resp,
   1.345 +					GSS_C_NO_CHANNEL_BINDINGS,&name,&mech,
   1.346 +					&chal,&flags,NIL,NIL);
   1.347 +				/* don't need response any more */
   1.348 +	  fs_give ((void **) &resp.value);
   1.349 +	  switch (smj) {	/* how did it go? */
   1.350 +	  case GSS_S_COMPLETE:	/* successful */
   1.351 +	  case GSS_S_CONTINUE_NEEDED:
   1.352 +	    if (chal.value) {	/* send challenge, get next response */
   1.353 +	      resp.value = (*responder) (chal.value,chal.length,
   1.354 +					 (unsigned long *) &resp.length);
   1.355 +	      gss_release_buffer (&smn,&chal);
   1.356 +	    }
   1.357 +	    break;
   1.358 +	  }
   1.359 +	}
   1.360 +	while (resp.value && resp.length && (smj == GSS_S_CONTINUE_NEEDED));
   1.361 +
   1.362 +				/* successful exchange? */
   1.363 +	if ((smj == GSS_S_COMPLETE) &&
   1.364 +	    (gss_display_name (&smn,name,&buf,&mech) == GSS_S_COMPLETE)) {
   1.365 +				/* send security and size */
   1.366 +	  memcpy (resp.value = tmp,(void *) &maxsize,resp.length = 4);
   1.367 +	  tmp[0] = AUTH_GSSAPI_P_NONE;
   1.368 +	  if (gss_wrap (&smn,ctx,NIL,qop,&resp,&conf,&chal) == GSS_S_COMPLETE){
   1.369 +	    resp.value = (*responder) (chal.value,chal.length,
   1.370 +				       (unsigned long *) &resp.length);
   1.371 +	    gss_release_buffer (&smn,&chal);
   1.372 +	    if (gss_unwrap (&smn,ctx,&resp,&chal,&conf,&qop)==GSS_S_COMPLETE) {
   1.373 +				/* client request valid */
   1.374 +	      if (chal.value && (chal.length > 4) &&
   1.375 +		  (chal.length < (MAILTMPLEN - 1)) &&
   1.376 +		  memcpy (tmp,chal.value,chal.length) &&
   1.377 +		  (tmp[0] & AUTH_GSSAPI_P_NONE)) {
   1.378 +				/* tie off authorization ID */
   1.379 +		tmp[chal.length] = '\0';
   1.380 +		ret = kerberos_login (tmp+4,buf.value,argc,argv);
   1.381 +	      }
   1.382 +				/* done with user name */
   1.383 +	      gss_release_buffer (&smn,&chal);
   1.384 +	    }
   1.385 +				/* finished with response */
   1.386 +	    fs_give ((void **) &resp.value);
   1.387 +	  }
   1.388 +				/* don't need name buffer any more */
   1.389 +	  gss_release_buffer (&smn,&buf);
   1.390 +	}
   1.391 +				/* don't need client name any more */
   1.392 +	gss_release_name (&smn,&name);
   1.393 +				/* don't need context any more */
   1.394 +	if (ctx != GSS_C_NO_CONTEXT) gss_delete_sec_context (&smn,&ctx,NIL);
   1.395 +      }
   1.396 +				/* finished with credentials */
   1.397 +      gss_release_cred (&smn,&crd);
   1.398 +    }
   1.399 +
   1.400 +    else {			/* can't acquire credentials! */
   1.401 +      if (gss_display_name (&dsmn,crname,&buf,&mech) == GSS_S_COMPLETE)
   1.402 +	SERVER_LOG ("Failed to acquire credentials for %s",buf.value);
   1.403 +      if (smj != GSS_S_FAILURE) do
   1.404 +	switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
   1.405 +					   GSS_C_NO_OID,&mctx,&resp)) {
   1.406 +	case GSS_S_COMPLETE:
   1.407 +	  mctx = 0;
   1.408 +	case GSS_S_CONTINUE_NEEDED:
   1.409 +	  SERVER_LOG ("Unknown GSSAPI failure: %s",resp.value);
   1.410 +	  gss_release_buffer (&dsmn,&resp);
   1.411 +	}
   1.412 +      while (dsmj == GSS_S_CONTINUE_NEEDED);
   1.413 +      do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
   1.414 +					    GSS_C_NO_OID,&mctx,&resp)) {
   1.415 +      case GSS_S_COMPLETE:
   1.416 +      case GSS_S_CONTINUE_NEEDED:
   1.417 +	SERVER_LOG ("GSSAPI mechanism status: %s",resp.value);
   1.418 +	gss_release_buffer (&dsmn,&resp);
   1.419 +      }
   1.420 +      while (dsmj == GSS_S_CONTINUE_NEEDED);
   1.421 +    }
   1.422 +				/* finished with credentials name */
   1.423 +    gss_release_name (&smn,&crname);
   1.424 +  }
   1.425 +  return ret;			/* return status */
   1.426 +}

UW-IMAP'd extensions by yuuji