Ejemplo n.º 1
0
long auth_gssapi_client (authchallenge_t challenger,authrespond_t responder,
			 char *service,NETMBX *mb,void *stream,
			 unsigned long *trial,char *user)
{
  gss_buffer_desc chal;
  kinit_t ki = (kinit_t) mail_parameters (NIL,GET_KINIT,NIL);
  long ret = NIL;
  *trial = 65535;		/* never retry */
				/* get initial (empty) challenge */
  if ((chal.value = (*challenger) (stream,(unsigned long *) &chal.length)) != NULL) {
    if (chal.length) {		/* abort if challenge non-empty */
      mm_log ("Server bug: non-empty initial GSSAPI challenge",WARN);
      (*responder) (stream,NIL,0);
      ret = LONGT;		/* will get a BAD response back */
    }
    else if (mb->authuser[0] && strcmp (mb->authuser,myusername ())) {
      mm_log ("Can't use Kerberos: invalid /authuser",WARN);
      (*responder) (stream,NIL,0);
      ret = LONGT;		/* will get a BAD response back */
    }
    else ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
					stream,user,ki);
  }
  return ret;
}
Ejemplo n.º 2
0
char *kerberos_login (char *user,char *authuser,int argc,char *argv[])
{
  krb5_context ctx;
  krb5_principal prnc;
  char kuser[NETMAXUSER];
  char *ret = NIL;
				/* make a context */
  if (!krb5_init_context (&ctx)) {
				/* build principal */
    if (!krb5_parse_name (ctx,authuser,&prnc)) {
				/* can get local name for this principal? */
      if (!krb5_aname_to_localname (ctx,prnc,NETMAXUSER-1,kuser)) {
				/* yes, local name permitted login as user?  */
	if (authserver_login (user,kuser,argc,argv) ||
	    authserver_login (lcase (user),kuser,argc,argv))
	  ret = myusername ();	/* yes, return user name */
      }
      krb5_free_principal (ctx,prnc);
    }
    krb5_free_context (ctx);	/* finished with context */
  }
  return ret;
}
Ejemplo n.º 3
0
sfs_connect_t *
sfs_connect_srp (str u, srp_client *srpp, sfs_connect_cb cb,
		 str *userp, str *pwdp, bool *serverokp)
{
  static rxx usrhost ("^([^@]+)?@(.*)$");
  if (!usrhost.match (u)) {
    if (userp)
      *userp = u;
    (*cb) (NULL, "not of form [user]@hostname");
    return NULL;
  }

  str user (usrhost[1]), host (usrhost[2]);
  if (!user && !(user = myusername ())) {
    (*cb) (NULL, "could not get local username");
    return NULL;
  }

  ref<sfssrp_authorizer> a (New refcounted<sfssrp_authorizer>);
  a->srpc = srpp;

  sfs_connect_t *cs
    = New sfs_connect_t (wrap (sfs_connect_srp_2, 
			       sfs::bundle_t<ref<sfssrp_authorizer>,
			       str *, str *> (a, userp, pwdp),
			       serverokp, cb));
  cs->sname () = host;
  cs->service () = SFS_AUTHSERV;
  cs->encrypt = true;
  cs->check_hostid = false;
  cs->authorizer = a;
  cs->aarg.user = user;
  if (!cs->start ())
    return NULL;
  return cs;
}
Ejemplo n.º 4
0
TCPSTREAM *tcp_aopen (NETMBX *mb,char *service,char *usrbuf)
{
  TCPSTREAM *stream = NIL;
  void *adr;
  char host[MAILTMPLEN],tmp[MAILTMPLEN],*path,*argv[MAXARGV+1],*r;
  int i,ti,pipei[2],pipeo[2];
  size_t len;
  time_t now;
  struct timeval tmo;
  fd_set fds,efds;
  blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
#ifdef SSHPATH			/* ssh path defined yet? */
  if (!sshpath) sshpath = cpystr (SSHPATH);
#endif
#ifdef RSHPATH			/* rsh path defined yet? */
  if (!rshpath) rshpath = cpystr (RSHPATH);
#endif
  if (*service == '*') {	/* want ssh? */
				/* return immediately if ssh disabled */
    if (!(sshpath && (ti = sshtimeout))) return NIL;
				/* ssh command prototype defined yet? */
    if (!sshcommand) sshcommand = cpystr ("%s %s -l %s exec /etc/r%sd");
  }
				/* want rsh? */
  else if (rshpath && (ti = rshtimeout)) {
				/* rsh command prototype defined yet? */
    if (!rshcommand) rshcommand = cpystr ("%s %s -l %s exec /etc/r%sd");
  }
  else return NIL;		/* rsh disabled */
				/* look like domain literal? */
  if (mb->host[0] == '[' && mb->host[i = (strlen (mb->host))-1] == ']') {
    strcpy (host,mb->host+1);	/* yes, copy without brackets */
    host[i-1] = '\0';
				/* validate domain literal */
    if (adr = ip_stringtoaddr (host,&len,&i)) fs_give ((void **) &adr);
    else {
      sprintf (tmp,"Bad format domain-literal: %.80s",host);
      mm_log (tmp,ERROR);
      return NIL;
    }
  }
  else strcpy (host,tcp_canonical (mb->host));

  if (*service == '*')		/* build ssh command */
    sprintf (tmp,sshcommand,sshpath,host,
	     mb->user[0] ? mb->user : myusername (),service + 1);
  else sprintf (tmp,rshcommand,rshpath,host,
		mb->user[0] ? mb->user : myusername (),service);
  if (tcpdebug) {
    char msg[MAILTMPLEN];
    sprintf (msg,"Trying %.100s",tmp);
    mm_log (msg,TCPDEBUG);
  }
				/* parse command into argv */
  for (i = 1,path = argv[0] = strtok_r (tmp," ",&r);
       (i < MAXARGV) && (argv[i] = strtok_r (NIL," ",&r)); i++);
  argv[i] = NIL;		/* make sure argv tied off */
				/* make command pipes */
  if (pipe (pipei) < 0) return NIL;
  if ((pipei[0] >= FD_SETSIZE) || (pipei[1] >= FD_SETSIZE) ||
      (pipe (pipeo) < 0)) {
    close (pipei[0]); close (pipei[1]);
    return NIL;
  }
  (*bn) (BLOCK_TCPOPEN,NIL);	/* quell alarm up here for NeXT */
  if ((pipeo[0] >= FD_SETSIZE) || (pipeo[1] >= FD_SETSIZE) ||
      ((i = fork ()) < 0)) {	/* make inferior process */
    close (pipei[0]); close (pipei[1]);
    close (pipeo[0]); close (pipeo[1]);
    (*bn) (BLOCK_NONE,NIL);
    return NIL;
  }
  if (!i) {			/* if child */
    alarm (0);			/* never have alarms in children */
    if (!fork ()) {		/* make grandchild so it's inherited by init */
      int cf;			/* don't alter parent vars in case vfork() */
      int maxfd = max (20,max (max(pipei[0],pipei[1]),max(pipeo[0],pipeo[1])));
      dup2 (pipei[1],1);	/* parent's input is my output */
      dup2 (pipei[1],2);	/* parent's input is my error output too */
      dup2 (pipeo[0],0);	/* parent's output is my input */
				/* close all unnecessary descriptors */
      for (cf = 3; cf <= maxfd; cf++) close (cf);
      setpgrp (0,getpid ());	/* be our own process group */
      _exit (execv (path,argv));/* now run it */
    }
    _exit (1);			/* child is done */
  }
  grim_pid_reap (i,NIL);	/* reap child; grandchild now owned by init */
  close (pipei[1]);		/* close child's side of the pipes */
  close (pipeo[0]);

				/* create TCP/IP stream */
  stream = (TCPSTREAM *) memset (fs_get (sizeof (TCPSTREAM)),0,
				 sizeof (TCPSTREAM));
				/* copy remote host name from argument */
  stream->remotehost = cpystr (stream->host = cpystr (host));
  stream->tcpsi = pipei[0];	/* init sockets */
  stream->tcpso = pipeo[1];
  stream->ictr = 0;		/* init input counter */
  stream->port = 0xffffffff;	/* no port number */
  ti += now = time (0);		/* open timeout */
  tmo.tv_usec = 0;		/* initialize usec timeout */
  FD_ZERO (&fds);		/* initialize selection vector */
  FD_ZERO (&efds);		/* handle errors too */
  FD_SET (stream->tcpsi,&fds);	/* set bit in selection vector */
  FD_SET (stream->tcpsi,&efds);	/* set bit in error selection vector */
  FD_SET (stream->tcpso,&efds);	/* set bit in error selection vector */
  do {				/* block under timeout */
    tmo.tv_sec = ti - now;
    i = select (max (stream->tcpsi,stream->tcpso)+1,&fds,NIL,&efds,&tmo);
    now = time (0);		/* fake timeout if interrupt & time expired */
    if ((i < 0) && (errno == EINTR) && ti && (ti <= now)) i = 0;
  } while ((i < 0) && (errno == EINTR));
  if (i <= 0) {			/* timeout or error? */
    sprintf (tmp,i ? "error in %s to IMAP server" :
	     "%s to IMAP server timed out",(*service == '*') ? "ssh" : "rsh");
    mm_log (tmp,WARN);
    tcp_close (stream);		/* punt stream */
    stream = NIL;
  }
  (*bn) (BLOCK_NONE,NIL);
				/* return user name */
  strcpy (usrbuf,mb->user[0] ? mb->user : myusername ());
  return stream;		/* return success */
}
Ejemplo n.º 5
0
int deliver (FILE *f,unsigned long msglen,char *user)
{
  MAILSTREAM *ds = NIL;
  char *s,*mailbox,tmp[MAILTMPLEN],path[MAILTMPLEN];
  STRING st;
  struct stat sbuf;
				/* have a mailbox specifier? */
  if ((mailbox = strchr (user,'+')) != NULL) {
    *mailbox++ = '\0';		/* yes, tie off user name */
    if (!*mailbox || !compare_cstring ((unsigned char *) mailbox,"INBOX"))
      mailbox = NIL;		/* user+ and user+INBOX same as user */
  }
  if (!*user) user = myusername ();
  else if (strcmp (user,myusername ()))
    return fail ("can't deliver to other user",EX_CANTCREAT);
  sprintf (tmp,"delivering to %.80s+%.80s",user,mailbox ? mailbox : "INBOX");
  mm_dlog (tmp);
				/* prepare stringstruct */
  INIT (&st,file_string,(void *) f,msglen);
  if (mailbox) {		/* non-INBOX name */
    switch (mailbox[0]) {	/* make sure a valid name */
    default:			/* other names, try to deliver if not INBOX */
      if ((strlen (mailbox) <= NETMAXMBX) &&
	  !strstr (mailbox,"..") && !strstr (mailbox,"//") &&
	  !strstr (mailbox,"/~") && mailboxfile (path,mailbox) && path[0] &&
	  !deliver_safely (NIL,&st,mailbox,path,tmp)) return NIL;
    case '%': case '*':		/* wildcards not valid */
    case '/':			/* absolute path names not valid */
    case '~':			/* user names not valid */
      sprintf (tmp,"invalid mailbox name %.80s+%.80s",user,mailbox);
      mm_log (tmp,WARN);
      break;
    }
    mm_dlog ("retrying delivery to INBOX");
    SETPOS (&st,0);		/* rewind stringstruct just in case */
  }

				/* no -I, resolve "INBOX" into path */
  if (mailboxfile (path,mailbox = "INBOX") && !path[0]) {
				/* clear box, get generic INBOX prototype */
    if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
      fatal ("no INBOX prototype");
				/* standard system driver? */
    if (!strcmp (ds->dtb->name,"unix") || !strcmp (ds->dtb->name,"mmdf")) {
      strcpy (path,sysinbox ());/* use system INBOX */
      if (!lstat (path,&sbuf))	/* deliver to existing system INBOX */
	return deliver_safely (ds,&st,mailbox,path,tmp);
    }
    else {			/* other driver, try ~/INBOX */
      if ((mailboxfile (path,"&&&&&") == path) &&
	  (s = strstr (path,"&&&&&")) && strcpy (s,"INBOX") &&
	  !lstat (path,&sbuf)){	/* deliver to existing ~/INBOX */
	sprintf (tmp,"#driver.%s/INBOX",ds->dtb->name);
	return deliver_safely (ds,&st,cpystr (tmp),path,tmp);
      }
    }
				/* not dummy, deliver to driver imputed path */
    if (strcmp (ds->dtb->name,"dummy"))
      return (ibxpath (ds,&mailbox,path) && !lstat (path,&sbuf)) ?
	deliver_safely (ds,&st,mailbox,path,tmp) :
	  fail ("unable to resolve INBOX path",EX_CANTCREAT);
				/* dummy, empty imputed append path exist? */
    if (ibxpath (ds = default_proto (T),&mailbox,path) &&
	!lstat (path,&sbuf) && !sbuf.st_size)
      return deliver_safely (ds,&st,mailbox,path,tmp);
				/* impute path that we will create */
    if (!ibxpath (ds = default_proto (NIL),&mailbox,path))
      return fail ("unable to resolve INBOX",EX_CANTCREAT);
  }
				/* black box, must create, get create proto */
  else if (lstat (path,&sbuf)) ds = default_proto (NIL);
  else {			/* black box, existing file */
				/* empty file, get append prototype */
    if (!sbuf.st_size) ds = default_proto (T);
				/* non-empty, get prototype from its data */
    else if (!(ds = mail_open (NIL,"INBOX",OP_PROTOTYPE)))
      fatal ("no INBOX prototype");
				/* error if unknown format */
    if (!strcmp (ds->dtb->name,"phile"))
      return fail ("unknown format INBOX",EX_UNAVAILABLE);
				/* otherwise can deliver to it */
    return deliver_safely (ds,&st,mailbox,path,tmp);
  }
  sprintf (tmp,"attempting to create mailbox %.80s path %.80s",mailbox,path);
  mm_dlog (tmp);
				/* supplicate to the Evil One */
  if (!path_create (ds,path)) return fail ("can't create INBOX",EX_CANTCREAT);
  sprintf (tmp,"created %.80s",path);
  mm_dlog (tmp);
				/* deliver the message */
  return deliver_safely (ds,&st,mailbox,path,tmp);
}
Ejemplo n.º 6
0
int main (int argc,char *argv[])
{
  FILE *f = NIL;
  int c,ret = 0;
  unsigned long msglen;
  char *s,tmp[MAILTMPLEN];
  uid_t ruid = getuid ();
  struct passwd *pwd = ruid ? getpwnam ("daemon") : NIL;
  openlog ("dmail",LOG_PID,LOG_MAIL);
				/* must not be root or daemon! */
  if (!ruid || (pwd && (pwd->pw_uid == ruid)))
    _exit (fail ("dmail may not be invoked by root or daemon",EX_USAGE));
#include "linkage.c"
				/* process all flags */
  for (--argc; argc && (*(s = *++argv)) == '-'; argc--) switch (s[1]) {
  case 'D':			/* debug */
    debug = T;			/* extra debugging */
    break;
  case 's':			/* deliver as seen */
    flagseen = T;
    break;
  case 'f':
  case 'r':			/* flag giving return path */
    if (sender) _exit (fail ("duplicate -r",EX_USAGE));
    if (argc--) sender = cpystr (*++argv);
    else _exit (fail ("missing argument to -r",EX_USAGE));
    break;
  case 'k':
    if (keywords) _exit (fail ("duplicate -k",EX_USAGE));
    if (argc--) keywords = cpystr (*++argv);
    else _exit (fail ("missing argument to -k",EX_USAGE));
    break;
  case 'p':
    if (s[2] && ((s[2] == '-') || isdigit (s[2]))) precedence = atol (s + 2);
    else if (argc-- && ((*(s = *++argv) == '-') || isdigit (*s)))
      precedence = atol (s);
    else _exit (fail ("missing argument to -p",EX_USAGE));
    break;
  default:			/* anything else */
    _exit (fail ("unknown switch",EX_USAGE));
  }

  if (argc > 1) _exit (fail ("too many recipients",EX_USAGE));
  else if (!(f = tmpfile ())) _exit(fail ("can't make temp file",EX_TEMPFAIL));
				/* build delivery headers */
  if (sender) fprintf (f,"Return-Path: <%s>\015\012",sender);
				/* start Received line: */
  fprintf (f,"Received: via dmail-%s.%s for %s; ",CCLIENTVERSION,version,
	   (argc == 1) ? *argv : myusername ());
  rfc822_date (tmp);
  fputs (tmp,f);
  fputs ("\015\012",f);
				/* copy text from standard input */
  if (!fgets (tmp,MAILTMPLEN-1,stdin) || !(s = strchr (tmp,'\n')) ||
      (s == tmp) || s[1]) _exit (fail ("bad first message line",EX_USAGE));
  else if (s[-1] == '\015') {	/* nuke leading "From " line */
    if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
	(tmp[3] != 'm') || (tmp[4] != ' ')) fputs (tmp,f);
    while ((c = getchar ()) != EOF) putc (c,f);
  }
  else {
    if ((tmp[0] != 'F') || (tmp[1] != 'r') || (tmp[2] != 'o') ||
	(tmp[3] != 'm') || (tmp[4] != ' ')) {
      *s++ = '\015';		/* overwrite NL with CRLF */
      *s++ = '\012';
      *s = '\0';		/* tie off string */
      fputs (tmp,f);		/* write line */
    }
  }
				/* copy text from standard input */
  while ((c = getchar ()) != EOF) {
				/* add CR if needed */
    if (c == '\012') putc ('\015',f);
    putc (c,f);
  }
  msglen = ftell (f);		/* size of message */
  fflush (f);			/* make sure all changes written out */
  if (ferror (f)) ret = fail ("error writing temp file",EX_TEMPFAIL);
  else if (!msglen) ret = fail ("empty message",EX_TEMPFAIL);
				/* single delivery */
  else ret = deliver (f,msglen,argc ? *argv : myusername ());
  fclose (f);			/* all done with temporary file */
  _exit (ret);			/* normal exit */
  return 0;			/* stupid gcc */
}
Ejemplo n.º 7
0
char *myhomedir ()
{
  if (!myHomeDir) myusername ();/* initialize if first time */
  return myHomeDir;
}
Ejemplo n.º 8
0
long auth_gssapi_client_work (authchallenge_t challenger,gss_buffer_desc chal,
			      authrespond_t responder,char *service,NETMBX *mb,
			      void *stream,char *user,kinit_t ki)
{
  char tmp[MAILTMPLEN];
  OM_uint32 smj,smn,dsmj,dsmn;
  OM_uint32 mctx = 0;
  gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
  gss_buffer_desc resp,buf;
  long i;
  int conf;
  gss_qop_t qop;
  gss_name_t crname = NIL;
  blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
  void *data;
  long ret = NIL;
  sprintf (tmp,"%s@%s",service,mb->host);
  buf.length = strlen (buf.value = tmp);
				/* get service name */
  if (gss_import_name (&smn,&buf,GSS_C_NT_HOSTBASED_SERVICE,&crname) !=
       GSS_S_COMPLETE) {
    mm_log ("Can't import Kerberos service name",WARN);
    (*responder) (stream,NIL,0);
  }
  else {
    data = (*bn) (BLOCK_SENSITIVE,NIL);
				/* negotiate with KDC */
    smj = gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,crname,NIL,
				GSS_C_INTEG_FLAG | GSS_C_MUTUAL_FLAG |
				GSS_C_REPLAY_FLAG,0,GSS_C_NO_CHANNEL_BINDINGS,
				GSS_C_NO_BUFFER,NIL,&resp,NIL,NIL);
    (*bn) (BLOCK_NONSENSITIVE,data);

				/* while continuation needed */
    while (smj == GSS_S_CONTINUE_NEEDED) {
      if (chal.value) fs_give ((void **) &chal.value);
				/* send response, get next challenge */
      i = (*responder) (stream,resp.value,resp.length) &&
	(chal.value = (*challenger) (stream,(unsigned long *) &chal.length));
      gss_release_buffer (&smn,&resp);
      if (i) {			/* negotiate continuation with KDC */
	data = (*bn) (BLOCK_SENSITIVE,NIL);
	switch (smj =		/* make sure continuation going OK */
		gss_init_sec_context (&smn,GSS_C_NO_CREDENTIAL,&ctx,
				      crname,GSS_C_NO_OID,GSS_C_INTEG_FLAG |
				      GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,0,
				      GSS_C_NO_CHANNEL_BINDINGS,&chal,NIL,
				      &resp,NIL,NIL)) {
	case GSS_S_CONTINUE_NEEDED:
	case GSS_S_COMPLETE:
	  break;
	default:		/* error, don't need context any more */
	  gss_delete_sec_context (&smn,&ctx,NIL);
	}
	(*bn) (BLOCK_NONSENSITIVE,data);
      }
      else {			/* error in continuation */
	mm_log ("Error in negotiating Kerberos continuation",WARN);
	(*responder) (stream,NIL,0);
				/* don't need context any more */
	gss_delete_sec_context (&smn,&ctx,NIL);
	break;
      }
    }

    switch (smj) {		/* done - deal with final condition */
    case GSS_S_COMPLETE:
      if (chal.value) fs_give ((void **) &chal.value);
				/* get prot mechanisms and max size */
      if ((*responder) (stream,resp.value ? resp.value : "",resp.length) &&
	  (chal.value = (*challenger) (stream,(unsigned long *)&chal.length))&&
	  (gss_unwrap (&smn,ctx,&chal,&resp,&conf,&qop) == GSS_S_COMPLETE) &&
	  (resp.length >= 4) && (*((char *) resp.value) & AUTH_GSSAPI_P_NONE)){
				/* make copy of flags and length */
	memcpy (tmp,resp.value,4);
	gss_release_buffer (&smn,&resp);
				/* no session protection */
	tmp[0] = AUTH_GSSAPI_P_NONE;
				/* install user name */
	strcpy (tmp+4,strcpy (user,mb->user[0] ? mb->user : myusername ()));
	buf.value = tmp; buf.length = strlen (user) + 4;
				/* successful negotiation */
	switch (smj = gss_wrap (&smn,ctx,NIL,qop,&buf,&conf,&resp)) {
	case GSS_S_COMPLETE:
	  if ((*responder) (stream,resp.value,resp.length)) ret = T;
	  gss_release_buffer (&smn,&resp);
	  break;
	default:
	  do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
						GSS_C_NO_OID,&mctx,&resp)) {
	  case GSS_S_COMPLETE:
	    mctx = 0;
	  case GSS_S_CONTINUE_NEEDED:
	    sprintf (tmp,"Unknown gss_wrap failure: %s",(char *) resp.value);
	    mm_log (tmp,WARN);
	    gss_release_buffer (&dsmn,&resp);
	  }
	  while (dsmj == GSS_S_CONTINUE_NEEDED);
	  do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
						GSS_C_NO_OID,&mctx,&resp)) {
	  case GSS_S_COMPLETE:
	  case GSS_S_CONTINUE_NEEDED:
	    sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value);
	    mm_log (tmp,WARN);
	    gss_release_buffer (&dsmn,&resp);
	  }
	  while (dsmj == GSS_S_CONTINUE_NEEDED);
	  (*responder) (stream,NIL,0);
	}
      }
				/* flush final challenge */
      if (chal.value) fs_give ((void **) &chal.value);
				/* don't need context any more */
      gss_delete_sec_context (&smn,&ctx,NIL);
      break;

    case GSS_S_CREDENTIALS_EXPIRED:
      if (chal.value) fs_give ((void **) &chal.value);
				/* retry if application kinits */
      if (ki && (*ki) (mb->host,"Kerberos credentials expired"))
	ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
				       stream,user,NIL);
      else {			/* application can't kinit */
	sprintf (tmp,"Kerberos credentials expired (try running kinit) for %s",
		 mb->host);
	mm_log (tmp,WARN);
	(*responder) (stream,NIL,0);
      }
      break;
    case GSS_S_FAILURE:
      if (chal.value) fs_give ((void **) &chal.value);
      do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
					    GSS_C_NO_OID,&mctx,&resp)) {
      case GSS_S_COMPLETE:	/* end of message, can kinit? */
	if (ki && kerberos_try_kinit (smn) &&
	    (*ki) (mb->host,(char *) resp.value)) {
	  gss_release_buffer (&dsmn,&resp);
	  ret = auth_gssapi_client_work (challenger,chal,responder,service,mb,
					 stream,user,NIL);
	  break;		/* done */
	}
	else (*responder) (stream,NIL,0);
      case GSS_S_CONTINUE_NEEDED:
	sprintf (tmp,kerberos_try_kinit (smn) ?
		 "Kerberos error: %.80s (try running kinit) for %.80s" :
		 "GSSAPI failure: %s for %.80s",(char *) resp.value,mb->host);
	mm_log (tmp,WARN);
	gss_release_buffer (&dsmn,&resp);
      } while (dsmj == GSS_S_CONTINUE_NEEDED);
      break;

    default:			/* miscellaneous errors */
      if (chal.value) fs_give ((void **) &chal.value);
      do switch (dsmj = gss_display_status (&dsmn,smj,GSS_C_GSS_CODE,
					    GSS_C_NO_OID,&mctx,&resp)) {
      case GSS_S_COMPLETE:
	mctx = 0;
      case GSS_S_CONTINUE_NEEDED:
	sprintf (tmp,"Unknown GSSAPI failure: %s",(char *) resp.value);
	mm_log (tmp,WARN);
	gss_release_buffer (&dsmn,&resp);
      }
      while (dsmj == GSS_S_CONTINUE_NEEDED);
      do switch (dsmj = gss_display_status (&dsmn,smn,GSS_C_MECH_CODE,
					    GSS_C_NO_OID,&mctx,&resp)) {
      case GSS_S_COMPLETE:
      case GSS_S_CONTINUE_NEEDED:
	sprintf (tmp,"GSSAPI mechanism status: %s",(char *) resp.value);
	mm_log (tmp,WARN);
	gss_release_buffer (&dsmn,&resp);
      }
      while (dsmj == GSS_S_CONTINUE_NEEDED);
      (*responder) (stream,NIL,0);
      break;
    }
				/* finished with credentials name */
    if (crname) gss_release_name (&smn,&crname);
  }
  return ret;			/* return status */
}