rle_read (FILE     *fp,
          guchar   *buf,
          tga_info *info)
{
  static gint   repeat = 0;
  static gint   direct = 0;
  static guchar sample[4];
  gint head;
  gint x, k;

  for (x = 0; x < info->width; x++)
    {
      if (repeat == 0 && direct == 0)
        {
          head = getc (fp);

          if (head == EOF)
            {
              return EOF;
            }
          else if (head >= 128)
            {
              repeat = head - 127;

              if (fread (sample, info->bytes, 1, fp) < 1)
                return EOF;
            }
          else
            {
              direct = head + 1;
            }
        }

      if (repeat > 0)
        {
          for (k = 0; k < info->bytes; ++k)
            {
              buf[k] = sample[k];
            }

          repeat--;
        }
      else /* direct > 0 */
        {
          if (fread (buf, info->bytes, 1, fp) < 1)
            return EOF;

          direct--;
        }

      buf += info->bytes;
    }

  return 0;
}


Fix possible NULL dereference (savannah bug #55369)

* src/sparse.c (pax_decode_header): Check return from find_next_block.

pax_decode_header (struct tar_sparse_file *file)
{
  if (file->stat_info->sparse_major > 0)
    {
      uintmax_t u;
      char nbuf[UINTMAX_STRSIZE_BOUND];
      union block *blk;
      char *p;
      size_t i;
      off_t start;

#define COPY_BUF(b,buf,src) do                                     \
 {                                                                 \
   char *endp = b->buffer + BLOCKSIZE;                             \
   char *dst = buf;                                                \
   do                                                              \
     {                                                             \
       if (dst == buf + UINTMAX_STRSIZE_BOUND -1)                  \
         {                                                         \
           ERROR ((0, 0, _("%s: numeric overflow in sparse archive member"), \
	          file->stat_info->orig_file_name));               \
           return false;                                           \
         }                                                         \
       if (src == endp)                                            \
	 {                                                         \
	   set_next_block_after (b);                               \
           b = find_next_block ();                                 \
           if (!b)                                                 \
             FATAL_ERROR ((0, 0, _("Unexpected EOF in archive"))); \
           src = b->buffer;                                        \
	   endp = b->buffer + BLOCKSIZE;                           \
	 }                                                         \
       *dst = *src++;                                              \
     }                                                             \
   while (*dst++ != '\n');                                         \
   dst[-1] = 0;                                                    \
 } while (0)

      start = current_block_ordinal ();
      set_next_block_after (current_header);
      blk = find_next_block ();
      if (!blk)
        FATAL_ERROR ((0, 0, _("Unexpected EOF in archive")));
      p = blk->buffer;
      COPY_BUF (blk,nbuf,p);
      if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
	{
	  ERROR ((0, 0, _("%s: malformed sparse archive member"),
		  file->stat_info->orig_file_name));
	  return false;
	}
      file->stat_info->sparse_map_size = u;
      file->stat_info->sparse_map = xcalloc (file->stat_info->sparse_map_size,
					     sizeof (*file->stat_info->sparse_map));
      file->stat_info->sparse_map_avail = 0;
      for (i = 0; i < file->stat_info->sparse_map_size; i++)
	{
	  struct sp_array sp;

	  COPY_BUF (blk,nbuf,p);
	  if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)))
	    {
	      ERROR ((0, 0, _("%s: malformed sparse archive member"),
		      file->stat_info->orig_file_name));
	      return false;
	    }
	  sp.offset = u;
	  COPY_BUF (blk,nbuf,p);
	  if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)))
	    {
	      ERROR ((0, 0, _("%s: malformed sparse archive member"),
		      file->stat_info->orig_file_name));
	      return false;
	    }
	  sp.numbytes = u;
	  sparse_add_map (file->stat_info, &sp);
	}
      set_next_block_after (blk);

      file->dumped_size += BLOCKSIZE * (current_block_ordinal () - start);
    }

  return true;
}

pax_decode_header in sparse.c in GNU Tar before 1.32 had a NULL pointer dereference when parsing certain archives that have malformed extended headers.


Submitted by: Tomas Hoger <thoger@redhat.com>

Fix for CVE-2010-0433 where some kerberos enabled versions of OpenSSL
could be crashed if the relevant tables were not present (e.g. chrooted).

krb5_kt_free_entry(krb5_context con,krb5_keytab_entry * entry)
        {
        if (!krb5_loaded)
                load_krb5_dll();

        if ( p_krb5_kt_free_entry )
                return(p_krb5_kt_free_entry(con,entry));
        else
                return KRB5KRB_ERR_GENERIC;
        }

The kssl_keytab_is_available function in ssl/kssl.c in OpenSSL before 0.9.8n, when Kerberos is enabled but Kerberos configuration files cannot be opened, does not check a certain return value, which allows remote attackers to cause a denial of service (NULL pointer dereference and daemon crash) via SSL cipher negotiation, as demonstrated by a chroot installation of Dovecot or stunnel without Kerberos configuration files inside the chroot.


kssl_krb5_auth_con_setrcache(krb5_context con, krb5_auth_context acon,
                             krb5_rcache rcache)
        {
        if ( p_krb5_auth_con_setrcache )
                 return(p_krb5_auth_con_setrcache(con,acon,rcache));
        else
                 return KRB5KRB_ERR_GENERIC;
        }


unsigned char	*kssl_skip_confound(krb5_enctype etype, unsigned char *a)
	{
	int 		i, conlen;
	size_t		cklen;
	static size_t 	*cksumlens = NULL;
	unsigned char	*test_auth;

	conlen = (etype)? 8: 0;

	if (!cksumlens  &&  !(cksumlens = populate_cksumlens()))  return NULL;
	for (i=0; (cklen = cksumlens[i]) != 0; i++)
		{
		test_auth = a + conlen + cklen;
		if (kssl_test_confound(test_auth))  return test_auth;
		}

	return NULL;
	}


kssl_ctx_setprinc(KSSL_CTX *kssl_ctx, int which,
        krb5_data *realm, krb5_data *entity, int nentities)
        {
	char	**princ;
	int 	length;
	int i;

	if (kssl_ctx == NULL  ||  entity == NULL)  return KSSL_CTX_ERR;

	switch (which)
                {
        case KSSL_CLIENT:	princ = &kssl_ctx->client_princ;	break;
        case KSSL_SERVER:	princ = &kssl_ctx->service_host;	break;
        default:		return KSSL_CTX_ERR;			break;
		}
	if (*princ)  kssl_free(*princ);

	/* Add up all the entity->lengths */
	length = 0;
	for (i=0; i < nentities; i++)
		{
		length += entity[i].length;
		}
	/* Add in space for the '/' character(s) (if any) */
	length += nentities-1;
	/* Space for the ('@'+realm+NULL | NULL) */
	length += ((realm)? realm->length + 2: 1);

	if ((*princ = kssl_calloc(1, length)) == NULL)
		return KSSL_CTX_ERR;
	else
		{
		for (i = 0; i < nentities; i++)
			{
			strncat(*princ, entity[i].data, entity[i].length);
			if (i < nentities-1)
				{
				strcat (*princ, "/");
				}
			}
		if (realm)
                        {
			strcat (*princ, "@");
			(void) strncat(*princ, realm->data, realm->length);
			}
		}

	return KSSL_CTX_OK;
        }


valid_cksumtype(krb5_cksumtype ctype)
        {
        if (!krb5_loaded)
                load_krb5_dll();

        if ( p_valid_cksumtype )
                return(p_valid_cksumtype(ctype));
        else
                return KRB5KRB_ERR_GENERIC;
        }


kssl_krb5_auth_con_getrcache(krb5_context con, krb5_auth_context acon,
                             krb5_rcache * prcache)
        {
	if ( p_krb5_auth_con_getrcache )
		return(p_krb5_auth_con_getrcache(con,acon, prcache));
	else
		return KRB5KRB_ERR_GENERIC;
	}


    kssl_keytab_is_available(KSSL_CTX *kssl_ctx)
{
    krb5_context		krb5context = NULL;
    krb5_keytab 		krb5keytab = NULL;
    krb5_keytab_entry           entry;
    krb5_principal              princ = NULL;
    krb5_error_code  		krb5rc = KRB5KRB_ERR_GENERIC;
    int rc = 0;

    if ((krb5rc = krb5_init_context(&krb5context)))
        return(0);

    /*	kssl_ctx->keytab_file == NULL ==> use Kerberos default
    */
    if (kssl_ctx->keytab_file)
    {
        krb5rc = krb5_kt_resolve(krb5context, kssl_ctx->keytab_file,
                                  &krb5keytab);
        if (krb5rc)
            goto exit;
    }
    else
    {
        krb5rc = krb5_kt_default(krb5context,&krb5keytab);
        if (krb5rc)
            goto exit;
    }

    /* the host key we are looking for */
    krb5rc = krb5_sname_to_principal(krb5context, NULL,
                                     kssl_ctx->service_name ? kssl_ctx->service_name: KRB5SVC,
                                     KRB5_NT_SRV_HST, &princ);

    if (krb5rc)
	goto exit;

    krb5rc = krb5_kt_get_entry(krb5context, krb5keytab,
                                princ,
                                0 /* IGNORE_VNO */,
                                0 /* IGNORE_ENCTYPE */,
                                &entry);
    if ( krb5rc == KRB5_KT_NOTFOUND ) {
        rc = 1;
        goto exit;
    } else if ( krb5rc )
        goto exit;

    krb5_kt_free_entry(krb5context, &entry);
    rc = 1;

  exit:
    if (krb5keytab)     krb5_kt_close(krb5context, krb5keytab);
    if (princ)          krb5_free_principal(krb5context, princ);
    if (krb5context)	krb5_free_context(krb5context);
    return(rc);
}


static void* kssl_calloc(size_t nmemb, size_t size)
{
	void* p;

	p=OPENSSL_malloc(nmemb*size);
	if (p){
		memset(p, 0, nmemb*size);
	}
	return p;
}


krb5_error_code  kssl_check_authent(
			/* IN     */	KSSL_CTX	*kssl_ctx,
                        /* IN     */   	krb5_data	*authentp,
			/* OUT    */	krb5_timestamp	*atimep,
			/* OUT    */    KSSL_ERR	*kssl_err  )
	{
        krb5_error_code		krb5rc = 0;
	KRB5_ENCDATA		*dec_authent = NULL;
	KRB5_AUTHENTBODY	*auth = NULL;
	krb5_enctype		enctype;
	EVP_CIPHER_CTX		ciph_ctx;
	const EVP_CIPHER	*enc = NULL;
	unsigned char		iv[EVP_MAX_IV_LENGTH];
	const unsigned char	*p;
	unsigned char		*unenc_authent;
	int 			outl, unencbufsize;
	struct tm		tm_time, *tm_l, *tm_g;
	time_t			now, tl, tg, tr, tz_offset;

	EVP_CIPHER_CTX_init(&ciph_ctx);
	*atimep = 0;
	kssl_err_set(kssl_err, 0, "");

#ifndef KRB5CHECKAUTH
	authentp = NULL;
#else
#if	KRB5CHECKAUTH == 0
	authentp = NULL;
#endif
#endif	/* KRB5CHECKAUTH */

	if (authentp == NULL  ||  authentp->length == 0)  return 0;

#ifdef KSSL_DEBUG
        {
        unsigned int ui;
	printf("kssl_check_authent: authenticator[%d]:\n",authentp->length);
	p = authentp->data;
	for (ui=0; ui < authentp->length; ui++)  printf("%02x ",p[ui]);
	printf("\n");
        }
#endif	/* KSSL_DEBUG */

	unencbufsize = 2 * authentp->length;
	if ((unenc_authent = calloc(1, unencbufsize)) == NULL)
		{
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
			"Unable to allocate authenticator buffer.\n");
		krb5rc = KRB5KRB_ERR_GENERIC;
		goto err;
		}

	p = (unsigned char *)authentp->data;
	if ((dec_authent = d2i_KRB5_ENCDATA(NULL, &p,
					(long) authentp->length)) == NULL)
		{
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "Error decoding authenticator.\n");
		krb5rc = KRB5KRB_AP_ERR_BAD_INTEGRITY;
		goto err;
		}

	enctype = dec_authent->etype->data[0];	/* should = kssl_ctx->enctype */
#if !defined(KRB5_MIT_OLD11)
            switch ( enctype ) {
            case ENCTYPE_DES3_CBC_SHA1:		/*    EVP_des_ede3_cbc();  */
            case ENCTYPE_DES3_CBC_SHA:
            case ENCTYPE_DES3_CBC_RAW:
                krb5rc = 0;                     /* Skip, can't handle derived keys */
                goto err;
            }
#endif
	enc = kssl_map_enc(enctype);
	memset(iv, 0, sizeof iv);       /* per RFC 1510 */

	if (enc == NULL)
		{
		/*  Disable kssl_check_authent for ENCTYPE_DES3_CBC_SHA1.
		**  This enctype indicates the authenticator was encrypted
		**  using key-usage derived keys which openssl cannot decrypt.
		*/
		goto err;
		}

        if (!EVP_CipherInit(&ciph_ctx,enc,kssl_ctx->key,iv,0))
                {
                kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "EVP_CipherInit error decrypting authenticator.\n");
                krb5rc = KRB5KRB_AP_ERR_BAD_INTEGRITY;
                goto err;
                }
        outl = dec_authent->cipher->length;
        if (!EVP_Cipher(&ciph_ctx,unenc_authent,dec_authent->cipher->data,outl))
                {
                kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "EVP_Cipher error decrypting authenticator.\n");
                krb5rc = KRB5KRB_AP_ERR_BAD_INTEGRITY;
                goto err;
                }
        EVP_CIPHER_CTX_cleanup(&ciph_ctx);

#ifdef KSSL_DEBUG
	printf("kssl_check_authent: decrypted authenticator[%d] =\n", outl);
	for (padl=0; padl < outl; padl++) printf("%02x ",unenc_authent[padl]);
	printf("\n");
#endif	/* KSSL_DEBUG */

	if ((p = kssl_skip_confound(enctype, unenc_authent)) == NULL)
		{
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "confounded by authenticator.\n");
		krb5rc = KRB5KRB_AP_ERR_BAD_INTEGRITY;
		goto err;
		}
	outl -= p - unenc_authent;

	if ((auth = (KRB5_AUTHENTBODY *) d2i_KRB5_AUTHENT(NULL, &p,
							  (long) outl))==NULL)
		{
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "Error decoding authenticator body.\n");
		krb5rc = KRB5KRB_AP_ERR_BAD_INTEGRITY;
		goto err;
		}

	memset(&tm_time,0,sizeof(struct tm));
	if (k_gmtime(auth->ctime, &tm_time)  &&
		((tr = mktime(&tm_time)) != (time_t)(-1)))
 		{
 		now  = time(&now);
 		tm_l = localtime(&now); 	tl = mktime(tm_l);
 		tm_g = gmtime(&now);		tg = mktime(tm_g);
 		tz_offset = tg - tl;

		*atimep = (krb5_timestamp)(tr - tz_offset);
 		}

#ifdef KSSL_DEBUG
	printf("kssl_check_authent: returns %d for client time ", *atimep);
	if (auth && auth->ctime && auth->ctime->length && auth->ctime->data)
		printf("%.*s\n", auth->ctime->length, auth->ctime->data);
	else	printf("NULL\n");
#endif	/* KSSL_DEBUG */

 err:
	if (auth)		KRB5_AUTHENT_free((KRB5_AUTHENT *) auth);
	if (dec_authent)	KRB5_ENCDATA_free(dec_authent);
	if (unenc_authent)	free(unenc_authent);
	EVP_CIPHER_CTX_cleanup(&ciph_ctx);
	return krb5rc;
	}


krb5_timeofday(krb5_context con, krb5_int32 *timeret)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_timeofday )
		return(p_krb5_timeofday(con,timeret));
	else
		return KRB5KRB_ERR_GENERIC;
	}


kssl_krb5_rd_req(krb5_context con, krb5_auth_context * pacon,
                krb5_const krb5_data * data,
                krb5_const_principal princ, krb5_keytab keytab,
                krb5_flags * flags, krb5_ticket ** pptkt)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_rd_req )
		return(p_krb5_rd_req(con,pacon,data,princ,keytab,flags,pptkt));
	else
		return KRB5KRB_ERR_GENERIC;
	}


*kstring(char *string)
        {
        static char	*null = "[NULL]";

	return ((string == NULL)? null: string);
        }


krb5_rc_default(krb5_context con, krb5_rcache *rc)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_rc_default )
		return(p_krb5_rc_default(con,rc));
	else
		return KRB5KRB_ERR_GENERIC;
	}


kssl_map_enc(krb5_enctype enctype)
        {
	switch (enctype)
		{
	case ENCTYPE_DES_HMAC_SHA1:		/*    EVP_des_cbc();       */
	case ENCTYPE_DES_CBC_CRC:
	case ENCTYPE_DES_CBC_MD4:
	case ENCTYPE_DES_CBC_MD5:
	case ENCTYPE_DES_CBC_RAW:
				return EVP_des_cbc();
				break;
	case ENCTYPE_DES3_CBC_SHA1:		/*    EVP_des_ede3_cbc();  */
	case ENCTYPE_DES3_CBC_SHA:
	case ENCTYPE_DES3_CBC_RAW:
				return EVP_des_ede3_cbc();
				break;
	default:                return NULL;
				break;
		}
	}


kssl_sget_tkt(	/* UPDATE */	KSSL_CTX		*kssl_ctx,
		/* IN     */	krb5_data		*indata,
		/* OUT    */	krb5_ticket_times	*ttimes,
		/* OUT    */	KSSL_ERR		*kssl_err  )
        {
        krb5_error_code			krb5rc = KRB5KRB_ERR_GENERIC;
        static krb5_context		krb5context = NULL;
	static krb5_auth_context	krb5auth_context = NULL;
	krb5_ticket 			*krb5ticket = NULL;
	KRB5_TKTBODY 			*asn1ticket = NULL;
	const unsigned char		*p;
	krb5_keytab 			krb5keytab = NULL;
	krb5_keytab_entry		kt_entry;
	krb5_principal			krb5server;
        krb5_rcache                     rcache = NULL;

	kssl_err_set(kssl_err, 0, "");

	if (!kssl_ctx)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
			"No kssl_ctx defined.\n");
		goto err;
		}

#ifdef KSSL_DEBUG
	printf("in kssl_sget_tkt(%s)\n", kstring(kssl_ctx->service_name));
#endif	/* KSSL_DEBUG */

	if (!krb5context  &&  (krb5rc = krb5_init_context(&krb5context)))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "krb5_init_context() fails.\n");
		goto err;
		}
	if (krb5auth_context  &&
		(krb5rc = krb5_auth_con_free(krb5context, krb5auth_context)))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "krb5_auth_con_free() fails.\n");
		goto err;
		}
	else  krb5auth_context = NULL;
	if (!krb5auth_context  &&
		(krb5rc = krb5_auth_con_init(krb5context, &krb5auth_context)))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "krb5_auth_con_init() fails.\n");
		goto err;
		}

	if ((krb5rc = krb5_auth_con_getrcache(krb5context, krb5auth_context,
		&rcache)))
		{
 		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
			"krb5_auth_con_getrcache() fails.\n");
 		goto err;
		}

	if ((krb5rc = krb5_sname_to_principal(krb5context, NULL,
                (kssl_ctx->service_name)? kssl_ctx->service_name: KRB5SVC,
                KRB5_NT_SRV_HST, &krb5server)) != 0)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "krb5_sname_to_principal() fails.\n");
		goto err;
		}

	if (rcache == NULL)
                {
                if ((krb5rc = krb5_get_server_rcache(krb5context,
			krb5_princ_component(krb5context, krb5server, 0),
			&rcache)))
                        {
		        kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                                "krb5_get_server_rcache() fails.\n");
                  	goto err;
                        }
                }

        if ((krb5rc = krb5_auth_con_setrcache(krb5context, krb5auth_context, rcache)))
                {
                kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
			"krb5_auth_con_setrcache() fails.\n");
                goto err;
                }

	/*	kssl_ctx->keytab_file == NULL ==> use Kerberos default
	*/
	if (kssl_ctx->keytab_file)
		{
		krb5rc = krb5_kt_resolve(krb5context, kssl_ctx->keytab_file,
                        &krb5keytab);
		if (krb5rc)
			{
			kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
				"krb5_kt_resolve() fails.\n");
			goto err;
			}
		}
	else
		{
                krb5rc = krb5_kt_default(krb5context,&krb5keytab);
                if (krb5rc)
			{
			kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
				"krb5_kt_default() fails.\n");
			goto err;
			}
		}

	/*	Actual Kerberos5 krb5_recvauth() has initial conversation here
	**	o	check KRB5_SENDAUTH_BADAUTHVERS
	**		unless KRB5_RECVAUTH_SKIP_VERSION
	**	o	check KRB5_SENDAUTH_BADAPPLVERS
	**	o	send "0" msg if all OK
	*/

	/*  20010411 was using AP_REQ instead of true KerberosWrapper
	**
	**  if ((krb5rc = krb5_rd_req(krb5context, &krb5auth_context,
	**			&krb5in_data, krb5server, krb5keytab,
	**			&ap_option, &krb5ticket)) != 0)  { Error }
	*/

	p = (unsigned char *)indata->data;
	if ((asn1ticket = (KRB5_TKTBODY *) d2i_KRB5_TICKET(NULL, &p,
						(long) indata->length)) == NULL)
		{
		BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
			"d2i_KRB5_TICKET() ASN.1 decode failure.\n");
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		goto err;
		}

	/* Was:  krb5rc = krb5_decode_ticket(krb5in_data,&krb5ticket)) != 0) */
	if ((krb5rc = kssl_TKT2tkt(krb5context, asn1ticket, &krb5ticket,
					kssl_err)) != 0)
		{
		BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
			"Error converting ASN.1 ticket to krb5_ticket.\n");
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		goto err;
		}

	if (! krb5_principal_compare(krb5context, krb5server,
						  krb5ticket->server))  {
		krb5rc = KRB5_PRINC_NOMATCH;
		BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
			"server principal != ticket principal\n");
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		goto err;
		}
	if ((krb5rc = krb5_kt_get_entry(krb5context, krb5keytab,
			krb5ticket->server, krb5ticket->enc_part.kvno,
			krb5ticket->enc_part.enctype, &kt_entry)) != 0)  {
		BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
			"krb5_kt_get_entry() fails with %x.\n", krb5rc);
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		goto err;
		}
	if ((krb5rc = krb5_decrypt_tkt_part(krb5context, &kt_entry.key,
			krb5ticket)) != 0)  {
		BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
			"krb5_decrypt_tkt_part() failed.\n");
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		goto err;
		}
	else  {
		krb5_kt_free_entry(krb5context, &kt_entry);
#ifdef KSSL_DEBUG
		{
		int i; krb5_address **paddr = krb5ticket->enc_part2->caddrs;
		printf("Decrypted ticket fields:\n");
		printf("\tflags: %X, transit-type: %X",
			krb5ticket->enc_part2->flags,
			krb5ticket->enc_part2->transited.tr_type);
		print_krb5_data("\ttransit-data: ",
			&(krb5ticket->enc_part2->transited.tr_contents));
		printf("\tcaddrs: %p, authdata: %p\n",
			krb5ticket->enc_part2->caddrs,
			krb5ticket->enc_part2->authorization_data);
		if (paddr)
			{
			printf("\tcaddrs:\n");
			for (i=0; paddr[i] != NULL; i++)
				{
				krb5_data d;
				d.length=paddr[i]->length;
				d.data=paddr[i]->contents;
				print_krb5_data("\t\tIP: ", &d);
				}
			}
		printf("\tstart/auth/end times: %d / %d / %d\n",
			krb5ticket->enc_part2->times.starttime,
			krb5ticket->enc_part2->times.authtime,
			krb5ticket->enc_part2->times.endtime);
		}
#endif	/* KSSL_DEBUG */
		}

	krb5rc = KRB5_NO_TKT_SUPPLIED;
	if (!krb5ticket  ||	!krb5ticket->enc_part2  ||
                !krb5ticket->enc_part2->client  ||
                !krb5ticket->enc_part2->client->data  ||
                !krb5ticket->enc_part2->session)
                {
                kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
                        "bad ticket from krb5_rd_req.\n");
		}
	else if (kssl_ctx_setprinc(kssl_ctx, KSSL_CLIENT,
		 &krb5ticket->enc_part2->client->realm,
		 krb5ticket->enc_part2->client->data,
		 krb5ticket->enc_part2->client->length))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
                        "kssl_ctx_setprinc() fails.\n");
		}
	else if (kssl_ctx_setkey(kssl_ctx, krb5ticket->enc_part2->session))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
                        "kssl_ctx_setkey() fails.\n");
		}
	else if (krb5ticket->enc_part2->flags & TKT_FLG_INVALID)
                {
		krb5rc = KRB5KRB_AP_ERR_TKT_INVALID;
                kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET,
                        "invalid ticket from krb5_rd_req.\n");
		}
	else	krb5rc = 0;

	kssl_ctx->enctype	= krb5ticket->enc_part.enctype;
	ttimes->authtime	= krb5ticket->enc_part2->times.authtime;
	ttimes->starttime	= krb5ticket->enc_part2->times.starttime;
	ttimes->endtime 	= krb5ticket->enc_part2->times.endtime;
	ttimes->renew_till	= krb5ticket->enc_part2->times.renew_till;

 err:
#ifdef KSSL_DEBUG
	kssl_ctx_show(kssl_ctx);
#endif	/* KSSL_DEBUG */

	if (asn1ticket) 	KRB5_TICKET_free((KRB5_TICKET *) asn1ticket);
        if (krb5keytab)         krb5_kt_close(krb5context, krb5keytab);
	if (krb5ticket) 	krb5_free_ticket(krb5context, krb5ticket);
	if (krb5server) 	krb5_free_principal(krb5context, krb5server);
	return (krb5rc);
        }


kssl_tgt_is_available(KSSL_CTX *kssl_ctx)
        {
        krb5_error_code		krb5rc = KRB5KRB_ERR_GENERIC;
        krb5_context		krb5context = NULL;
        krb5_ccache 		krb5ccdef = NULL;
        krb5_creds		krb5creds, *krb5credsp = NULL;
        int                     rc = 0;

        memset((char *)&krb5creds, 0, sizeof(krb5creds));

        if (!kssl_ctx)
            return(0);

        if (!kssl_ctx->service_host)
            return(0);

        if ((krb5rc = krb5_init_context(&krb5context)) != 0)
            goto err;

        if ((krb5rc = krb5_sname_to_principal(krb5context,
                                              kssl_ctx->service_host,
                                              (kssl_ctx->service_name)? kssl_ctx->service_name: KRB5SVC,
                                              KRB5_NT_SRV_HST, &krb5creds.server)) != 0)
            goto err;

        if ((krb5rc = krb5_cc_default(krb5context, &krb5ccdef)) != 0)
            goto err;

        if ((krb5rc = krb5_cc_get_principal(krb5context, krb5ccdef,
                                             &krb5creds.client)) != 0)
            goto err;

        if ((krb5rc = krb5_get_credentials(krb5context, 0, krb5ccdef,
                                            &krb5creds, &krb5credsp)) != 0)
            goto err;

        rc = 1;

      err:
#ifdef KSSL_DEBUG
	kssl_ctx_show(kssl_ctx);
#endif	/* KSSL_DEBUG */

	if (krb5creds.client)	krb5_free_principal(krb5context, krb5creds.client);
	if (krb5creds.server)	krb5_free_principal(krb5context, krb5creds.server);
	if (krb5context)	krb5_free_context(krb5context);
        return(rc);
	}


static int 	kssl_test_confound(unsigned char *p)
	{
	int 	len = 2;
	int 	xx = 0, yy = 0;

	if (*p++ != 0x62)  return 0;
	if (*p > 0x82)  return 0;
	switch(*p)  {
		case 0x82:  p++;          xx = (*p++ << 8);  xx += *p++;  break;
		case 0x81:  p++;          xx =  *p++;  break;
		case 0x80:  return 0;
		default:    xx = *p++;  break;
		}
	if (*p++ != 0x30)  return 0;
	if (*p > 0x82)  return 0;
	switch(*p)  {
		case 0x82:  p++; len+=2;  yy = (*p++ << 8);  yy += *p++;  break;
		case 0x81:  p++; len++;   yy =  *p++;  break;
		case 0x80:  return 0;
		default:    yy = *p++;  break;
		}

	return (xx - len == yy)? 1: 0;
	}


static size_t  *populate_cksumlens(void)
	{
	int 		i, j, n;
	static size_t 	*cklens = NULL;

#ifdef KRB5_MIT_OLD11
	n = krb5_max_cksum;
#else
	n = 0x0010;
#endif	/* KRB5_MIT_OLD11 */

#ifdef KRB5CHECKAUTH
	if (!cklens && !(cklens = (size_t *) calloc(sizeof(int),n+1)))  return NULL;

	for (i=0; i < n; i++)  {
		if (!valid_cksumtype(i))  continue;	/*  array has holes  */
		for (j=0; j < n; j++)  {
			if (cklens[j] == 0)  {
				cklens[j] = krb5_checksum_size(NULL,i);
				break;		/*  krb5 elem was new: add   */
				}
			if (cklens[j] == krb5_checksum_size(NULL,i))  {
				break;		/*  ignore duplicate elements */
				}
			}
		}
#endif	/* KRB5CHECKAUTH */

	return cklens;
	}


kssl_TKT2tkt(	/* IN     */	krb5_context	krb5context,
		/* IN     */	KRB5_TKTBODY	*asn1ticket,
		/* OUT    */	krb5_ticket	**krb5ticket,
		/* OUT    */	KSSL_ERR *kssl_err  )
        {
        krb5_error_code			krb5rc = KRB5KRB_ERR_GENERIC;
	krb5_ticket 			*new5ticket = NULL;
	ASN1_GENERALSTRING		*gstr_svc, *gstr_host;

	*krb5ticket = NULL;

	if (asn1ticket == NULL  ||  asn1ticket->realm == NULL  ||
		asn1ticket->sname == NULL  ||
		sk_ASN1_GENERALSTRING_num(asn1ticket->sname->namestring) < 2)
		{
		BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
			"Null field in asn1ticket.\n");
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		return KRB5KRB_ERR_GENERIC;
		}

	if ((new5ticket = (krb5_ticket *) calloc(1, sizeof(krb5_ticket)))==NULL)
		{
		BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
			"Unable to allocate new krb5_ticket.\n");
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		return ENOMEM;		/*  or  KRB5KRB_ERR_GENERIC;	*/
		}

	gstr_svc  = sk_ASN1_GENERALSTRING_value(asn1ticket->sname->namestring, 0);
	gstr_host = sk_ASN1_GENERALSTRING_value(asn1ticket->sname->namestring, 1);

	if ((krb5rc = kssl_build_principal_2(krb5context,
			&new5ticket->server,
			asn1ticket->realm->length, (char *)asn1ticket->realm->data,
			gstr_svc->length,  (char *)gstr_svc->data,
			gstr_host->length, (char *)gstr_host->data)) != 0)
		{
		free(new5ticket);
		BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
			"Error building ticket server principal.\n");
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		return krb5rc;		/*  or  KRB5KRB_ERR_GENERIC;	*/
		}

	krb5_princ_type(krb5context, new5ticket->server) =
			asn1ticket->sname->nametype->data[0];
	new5ticket->enc_part.enctype = asn1ticket->encdata->etype->data[0];
	new5ticket->enc_part.kvno = asn1ticket->encdata->kvno->data[0];
	new5ticket->enc_part.ciphertext.length =
			asn1ticket->encdata->cipher->length;
	if ((new5ticket->enc_part.ciphertext.data =
		calloc(1, asn1ticket->encdata->cipher->length)) == NULL)
		{
		free(new5ticket);
		BIO_snprintf(kssl_err->text, KSSL_ERR_MAX,
			"Error allocating cipher in krb5ticket.\n");
		kssl_err->reason = SSL_R_KRB5_S_RD_REQ;
		return KRB5KRB_ERR_GENERIC;
		}
	else
		{
		memcpy(new5ticket->enc_part.ciphertext.data,
			asn1ticket->encdata->cipher->data,
			asn1ticket->encdata->cipher->length);
		}

	*krb5ticket = new5ticket;
	return 0;
	}


krb5_error_code  kssl_build_principal_2(
			/* UPDATE */	krb5_context	context,
			/* OUT    */	krb5_principal	*princ,
			/* IN     */	int rlen,  const char *realm,
			/* IN	  */	int slen,  const char *svc,
			/* IN	  */	int hlen,  const char *host)
	{
	krb5_data		*p_data = NULL;
	krb5_principal		new_p = NULL;
        char			*new_r = NULL;

	if ((p_data = (krb5_data *) calloc(2, sizeof(krb5_data))) == NULL  ||
	    (new_p = (krb5_principal) calloc(1, sizeof(krb5_principal_data)))
			== NULL)  goto err;
	new_p->length = 2;
	new_p->data = p_data;

	if ((new_r = calloc(1, rlen + 1)) == NULL)  goto err;
	memcpy(new_r, realm, rlen);
	krb5_princ_set_realm_length(context, new_p, rlen);
	krb5_princ_set_realm_data(context, new_p, new_r);

	if ((new_p->data[0].data = calloc(1, slen + 1)) == NULL)  goto err;
	memcpy(new_p->data[0].data, svc, slen);
	new_p->data[0].length = slen;

	if ((new_p->data[1].data = calloc(1, hlen + 1)) == NULL)  goto err;
	memcpy(new_p->data[1].data, host, hlen);
	new_p->data[1].length = hlen;

	krb5_princ_type(context, new_p) = KRB5_NT_UNKNOWN;
	*princ = new_p;
	return 0;

 err:
	if (new_p  &&  new_p[0].data)	free(new_p[0].data);
	if (new_p  &&  new_p[1].data)	free(new_p[1].data);
	if (new_p)	free(new_p);
	if (new_r)	free(new_r);
	return ENOMEM;
	}


krb5_rc_destroy(krb5_context con, krb5_rcache rc)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_rc_destroy )
		return(p_krb5_rc_destroy(con, rc));
	else
		return KRB5KRB_ERR_GENERIC;
	}


kssl_ctx_show(KSSL_CTX *kssl_ctx)
        {
	int 	i;

	printf("kssl_ctx: ");
	if (kssl_ctx == NULL)
                {
		printf("NULL\n");
		return;
		}
	else
		printf("%p\n", (void *)kssl_ctx);

	printf("\tservice:\t%s\n",
                (kssl_ctx->service_name)? kssl_ctx->service_name: "NULL");
	printf("\tclient:\t%s\n",
                (kssl_ctx->client_princ)? kssl_ctx->client_princ: "NULL");
	printf("\tserver:\t%s\n",
                (kssl_ctx->service_host)? kssl_ctx->service_host: "NULL");
	printf("\tkeytab:\t%s\n",
                (kssl_ctx->keytab_file)? kssl_ctx->keytab_file: "NULL");
	printf("\tkey [%d:%d]:\t",
                kssl_ctx->enctype, kssl_ctx->length);

	for (i=0; i < kssl_ctx->length  &&  kssl_ctx->key; i++)
                {
		printf("%02x", kssl_ctx->key[i]);
		}
	printf("\n");
	return;
        }


kssl_ctx_setkey(KSSL_CTX *kssl_ctx, krb5_keyblock *session)
        {
	int 		length;
	krb5_enctype	enctype;
	krb5_octet FAR	*contents = NULL;

	if (!kssl_ctx)  return KSSL_CTX_ERR;

	if (kssl_ctx->key)
                {
		OPENSSL_cleanse(kssl_ctx->key, kssl_ctx->length);
		kssl_free(kssl_ctx->key);
		}

	if (session)
                {

#ifdef KRB5_HEIMDAL
		length = session->keyvalue->length;
		enctype = session->keytype;
		contents = session->keyvalue->contents;
#else
		length = session->length;
		enctype = session->enctype;
		contents = session->contents;
#endif
		kssl_ctx->enctype = enctype;
		kssl_ctx->length  = length;
		}
	else
                {
		kssl_ctx->enctype = ENCTYPE_UNKNOWN;
		kssl_ctx->length  = 0;
		return KSSL_CTX_OK;
		}

	if ((kssl_ctx->key =
                (krb5_octet FAR *) kssl_calloc(1, kssl_ctx->length)) == NULL)
                {
		kssl_ctx->length  = 0;
		return KSSL_CTX_ERR;
		}
	else
		memcpy(kssl_ctx->key, contents, length);

	return KSSL_CTX_OK;
        }


kssl_ctx_setstring(KSSL_CTX *kssl_ctx, int which, char *text)
        {
	char	**string;

	if (!kssl_ctx)  return KSSL_CTX_ERR;

	switch (which)
                {
        case KSSL_SERVICE:	string = &kssl_ctx->service_name;	break;
        case KSSL_SERVER:	string = &kssl_ctx->service_host;	break;
        case KSSL_CLIENT:	string = &kssl_ctx->client_princ;	break;
        case KSSL_KEYTAB:	string = &kssl_ctx->keytab_file;	break;
        default:		return KSSL_CTX_ERR;			break;
		}
	if (*string)  kssl_free(*string);

	if (!text)
                {
		*string = '\0';
		return KSSL_CTX_OK;
		}

	if ((*string = kssl_calloc(1, strlen(text) + 1)) == NULL)
		return KSSL_CTX_ERR;
	else
		strcpy(*string, text);

	return KSSL_CTX_OK;
        }


krb5_principal_compare(krb5_context con, krb5_const_principal princ1,
                krb5_const_principal princ2)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_principal_compare )
		return(p_krb5_principal_compare(con,princ1,princ2));
	else
		return KRB5KRB_ERR_GENERIC;
	}


kssl_krb5_kt_get_entry(krb5_context context, krb5_keytab keytab,
                       krb5_const_principal principal, krb5_kvno vno,
                       krb5_enctype enctype, krb5_keytab_entry *entry)
	{
	if ( p_krb5_kt_get_entry )
		return(p_krb5_kt_get_entry(context,keytab,principal,vno,enctype,entry));
	else
		return KRB5KRB_ERR_GENERIC;
        }


krb5_checksum_size(krb5_context context,krb5_cksumtype ctype)
        {
        if (!krb5_loaded)
                load_krb5_dll();

        if ( p_krb5_checksum_size )
                return(p_krb5_checksum_size(context, ctype));
        else
                return KRB5KRB_ERR_GENERIC;
        }


krb5_decrypt_tkt_part(krb5_context con, krb5_const krb5_keyblock *keys,
                krb5_ticket *ticket)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_decrypt_tkt_part )
		return(p_krb5_decrypt_tkt_part(con,keys,ticket));
	else
		return KRB5KRB_ERR_GENERIC;
	}


kssl_krb5_kt_close(krb5_context context, krb5_keytab keytab)
	{
	if ( p_krb5_kt_close )
		return(p_krb5_kt_close(context,keytab));
	else
		return KRB5KRB_ERR_GENERIC;
	}


static krb5_deltat get_rc_clockskew(krb5_context context)
	{
	krb5_rcache 	rc;
	krb5_deltat 	clockskew;

	if (krb5_rc_default(context, &rc))  return KSSL_CLOCKSKEW;
	if (krb5_rc_initialize(context, rc, 0))  return KSSL_CLOCKSKEW;
	if (krb5_rc_get_lifespan(context, rc, &clockskew))  {
		clockskew = KSSL_CLOCKSKEW;
		}
	(void) krb5_rc_destroy(context, rc);
	return clockskew;
	}


kssl_krb5_get_credentials(krb5_context CO,
                         krb5_const krb5_flags F,
                         krb5_ccache CC,
                         krb5_creds  * pCR,
                         krb5_creds  ** ppCR)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_get_credentials )
		return(p_krb5_get_credentials(CO,F,CC,pCR,ppCR));
	else
		return KRB5KRB_ERR_GENERIC;
	}


kssl_krb5_mk_req_extended (krb5_context CO,
                          krb5_auth_context  * pACO,
                          krb5_const krb5_flags F,
                          krb5_data  * pD1,
                          krb5_creds  * pC,
                          krb5_data  * pD2)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_mk_req_extended )
		return(p_krb5_mk_req_extended(CO,pACO,F,pD1,pC,pD2));
	else
		return KRB5KRB_ERR_GENERIC;
	}


kssl_krb5_free_ticket(krb5_context con,
                     krb5_ticket * kt)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_free_ticket )
		return(p_krb5_free_ticket(con,kt));
	else
		return KRB5KRB_ERR_GENERIC;
	}


kssl_krb5_free_principal(krb5_context c, krb5_principal p)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_free_principal )
		p_krb5_free_principal(c,p);
	}


kssl_krb5_get_server_rcache(krb5_context con, krb5_const krb5_data * data,
                            krb5_rcache * rcache)
        {
	if ( p_krb5_get_server_rcache )
		return(p_krb5_get_server_rcache(con,data,rcache));
	else
		return KRB5KRB_ERR_GENERIC;
        }


kssl_krb5_free_context(krb5_context CO)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_free_context )
		p_krb5_free_context(CO);
	}


kssl_krb5_auth_con_free (krb5_context CO,
                        krb5_auth_context ACO)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_auth_con_free )
		return(p_krb5_auth_con_free(CO,ACO));
	else
		return KRB5KRB_ERR_GENERIC;
	}


print_krb5_authdata(char *label, krb5_authdata **adata)
        {
	if (adata == NULL)
                {
		printf("%s, authdata==0\n", label);
		return;
		}
	printf("%s [%p]\n", label, (void *)adata);
#if 0
	{
        int 	i;
	printf("%s[at%d:%d] ", label, adata->ad_type, adata->length);
	for (i=0; i < adata->length; i++)
                {
                printf((isprint(adata->contents[i]))? "%c ": "%02x",
                        adata->contents[i]);
		}
	printf("\n");
	}
#endif
	}


kssl_cget_tkt(	/* UPDATE */	KSSL_CTX *kssl_ctx,
                /* OUT    */	krb5_data **enc_ticketp,
                /* UPDATE */	krb5_data *authenp,
                /* OUT    */	KSSL_ERR *kssl_err)
	{
	krb5_error_code		krb5rc = KRB5KRB_ERR_GENERIC;
	krb5_context		krb5context = NULL;
	krb5_auth_context	krb5auth_context = NULL;
	krb5_ccache 		krb5ccdef = NULL;
	krb5_creds		krb5creds, *krb5credsp = NULL;
	krb5_data		krb5_app_req;

	kssl_err_set(kssl_err, 0, "");
	memset((char *)&krb5creds, 0, sizeof(krb5creds));

	if (!kssl_ctx)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "No kssl_ctx defined.\n");
		goto err;
		}
	else if (!kssl_ctx->service_host)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT,
                        "kssl_ctx service_host undefined.\n");
		goto err;
		}

	if ((krb5rc = krb5_init_context(&krb5context)) != 0)
                {
		BIO_snprintf(kssl_err->text,KSSL_ERR_MAX,
                        "krb5_init_context() fails: %d\n", krb5rc);
		kssl_err->reason = SSL_R_KRB5_C_INIT;
		goto err;
		}

	if ((krb5rc = krb5_sname_to_principal(krb5context,
                kssl_ctx->service_host,
                (kssl_ctx->service_name)? kssl_ctx->service_name: KRB5SVC,
                KRB5_NT_SRV_HST, &krb5creds.server)) != 0)
                {
		BIO_snprintf(kssl_err->text,KSSL_ERR_MAX,
                        "krb5_sname_to_principal() fails for %s/%s\n",
                        kssl_ctx->service_host,
                        (kssl_ctx->service_name)? kssl_ctx->service_name:
						  KRB5SVC);
		kssl_err->reason = SSL_R_KRB5_C_INIT;
		goto err;
		}

	if ((krb5rc = krb5_cc_default(krb5context, &krb5ccdef)) != 0)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_C_CC_PRINC,
                        "krb5_cc_default fails.\n");
		goto err;
		}

	if ((krb5rc = krb5_cc_get_principal(krb5context, krb5ccdef,
                &krb5creds.client)) != 0)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_C_CC_PRINC,
                        "krb5_cc_get_principal() fails.\n");
		goto err;
		}

	if ((krb5rc = krb5_get_credentials(krb5context, 0, krb5ccdef,
                &krb5creds, &krb5credsp)) != 0)
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_C_GET_CRED,
                        "krb5_get_credentials() fails.\n");
		goto err;
		}

	*enc_ticketp = &krb5credsp->ticket;
#ifdef KRB5_HEIMDAL
	kssl_ctx->enctype = krb5credsp->session.keytype;
#else
	kssl_ctx->enctype = krb5credsp->keyblock.enctype;
#endif

	krb5rc = KRB5KRB_ERR_GENERIC;
	/*	caller should free data of krb5_app_req  */
	/*  20010406 VRS deleted for real KerberosWrapper
	**  20010605 VRS reinstated to offer Authenticator to KerberosWrapper
	*/
	krb5_app_req.length = 0;
	if (authenp)
                {
		krb5_data	krb5in_data;
		const unsigned char	*p;
		long		arlen;
		KRB5_APREQBODY	*ap_req;

		authenp->length = 0;
		krb5in_data.data = NULL;
		krb5in_data.length = 0;
		if ((krb5rc = krb5_mk_req_extended(krb5context,
			&krb5auth_context, 0, &krb5in_data, krb5credsp,
			&krb5_app_req)) != 0)
			{
			kssl_err_set(kssl_err, SSL_R_KRB5_C_MK_REQ,
				"krb5_mk_req_extended() fails.\n");
			goto err;
			}

		arlen = krb5_app_req.length;
		p = (unsigned char *)krb5_app_req.data;
		ap_req = (KRB5_APREQBODY *) d2i_KRB5_APREQ(NULL, &p, arlen);
		if (ap_req)
			{
			authenp->length = i2d_KRB5_ENCDATA(
					ap_req->authenticator, NULL);
			if (authenp->length  &&
				(authenp->data = malloc(authenp->length)))
				{
				unsigned char	*adp = (unsigned char *)authenp->data;
				authenp->length = i2d_KRB5_ENCDATA(
						ap_req->authenticator, &adp);
				}
			}

		if (ap_req)  KRB5_APREQ_free((KRB5_APREQ *) ap_req);
		if (krb5_app_req.length)
                        kssl_krb5_free_data_contents(krb5context,&krb5_app_req);
		}
#ifdef KRB5_HEIMDAL
	if (kssl_ctx_setkey(kssl_ctx, &krb5credsp->session))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_C_INIT,
                        "kssl_ctx_setkey() fails.\n");
		}
#else
	if (kssl_ctx_setkey(kssl_ctx, &krb5credsp->keyblock))
                {
		kssl_err_set(kssl_err, SSL_R_KRB5_C_INIT,
                        "kssl_ctx_setkey() fails.\n");
		}
#endif
	else	krb5rc = 0;

 err:
#ifdef KSSL_DEBUG
	kssl_ctx_show(kssl_ctx);
#endif	/* KSSL_DEBUG */

	if (krb5creds.client)	krb5_free_principal(krb5context,
							krb5creds.client);
	if (krb5creds.server)	krb5_free_principal(krb5context,
							krb5creds.server);
	if (krb5auth_context)	krb5_auth_con_free(krb5context,
							krb5auth_context);
	if (krb5context)	krb5_free_context(krb5context);
	return (krb5rc);
	}


kssl_err_set(KSSL_ERR *kssl_err, int reason, char *text)
        {
	if (kssl_err == NULL)  return;

	kssl_err->reason = reason;
	BIO_snprintf(kssl_err->text, KSSL_ERR_MAX, "%s", text);
	return;
        }


print_krb5_keyblock(char *label, krb5_keyblock *keyblk)
        {
	int i;

	if (keyblk == NULL)
                {
		printf("%s, keyblk==0\n", label);
		return;
		}
#ifdef KRB5_HEIMDAL
	printf("%s\n\t[et%d:%d]: ", label, keyblk->keytype,
					   keyblk->keyvalue->length);
	for (i=0; i < (int)keyblk->keyvalue->length; i++)
                {
		printf("%02x",(unsigned char *)(keyblk->keyvalue->contents)[i]);
		}
	printf("\n");
#else
	printf("%s\n\t[et%d:%d]: ", label, keyblk->enctype, keyblk->length);
	for (i=0; i < (int)keyblk->length; i++)
                {
		printf("%02x",keyblk->contents[i]);
		}
	printf("\n");
#endif
        }


print_krb5_princ(char *label, krb5_principal_data *princ)
        {
	int i, ui, uj;

	printf("%s principal Realm: ", label);
	if (princ == NULL)  return;
	for (ui=0; ui < (int)princ->realm.length; ui++)  putchar(princ->realm.data[ui]);
	printf(" (nametype %d) has %d strings:\n", princ->type,princ->length);
	for (i=0; i < (int)princ->length; i++)
                {
		printf("\t%d [%d]: ", i, princ->data[i].length);
		for (uj=0; uj < (int)princ->data[i].length; uj++)  {
			putchar(princ->data[i].data[uj]);
			}
		printf("\n");
		}
	return;
        }


kssl_krb5_kt_default(krb5_context con,
                    krb5_keytab * kt)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_kt_default )
		return(p_krb5_kt_default(con,kt));
	else
		return KRB5KRB_ERR_GENERIC;
	}


kssl_krb5_init_context(krb5_context * pCO)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_init_context )
		return(p_krb5_init_context(pCO));
	else
		return KRB5KRB_ERR_GENERIC;
	}


krb5_rc_get_lifespan(krb5_context con, krb5_rcache rc, krb5_deltat *lifespanp)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_rc_get_lifespan )
		return(p_krb5_rc_get_lifespan(con, rc, lifespanp));
	else
		return KRB5KRB_ERR_GENERIC;
	}


krb5_rc_initialize(krb5_context con, krb5_rcache rc, krb5_deltat lifespan)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_rc_initialize )
		return(p_krb5_rc_initialize(con, rc, lifespan));
	else
		return KRB5KRB_ERR_GENERIC;
	}


static struct tm *k_gmtime(ASN1_GENERALIZEDTIME *gtime, struct tm *k_tm)
	{
	char 		c, *p;

	if (!k_tm)  return NULL;
	if (gtime == NULL  ||  gtime->length < 14)  return NULL;
	if (gtime->data == NULL)  return NULL;

	p = (char *)&gtime->data[14];

	c = *p;	 *p = '\0';  p -= 2;  k_tm->tm_sec  = atoi(p);      *(p+2) = c;
	c = *p;	 *p = '\0';  p -= 2;  k_tm->tm_min  = atoi(p);      *(p+2) = c;
	c = *p;	 *p = '\0';  p -= 2;  k_tm->tm_hour = atoi(p);      *(p+2) = c;
	c = *p;	 *p = '\0';  p -= 2;  k_tm->tm_mday = atoi(p);      *(p+2) = c;
	c = *p;	 *p = '\0';  p -= 2;  k_tm->tm_mon  = atoi(p)-1;    *(p+2) = c;
	c = *p;	 *p = '\0';  p -= 4;  k_tm->tm_year = atoi(p)-1900; *(p+4) = c;

	return k_tm;
	}


kssl_krb5_sname_to_principal(krb5_context CO,
                            krb5_const char  * pC1,
                            krb5_const char  * pC2,
                            krb5_int32 I,
                            krb5_principal  * pPR)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_sname_to_principal )
		return(p_krb5_sname_to_principal(CO,pC1,pC2,I,pPR));
	else
		return KRB5KRB_ERR_GENERIC;
	}


load_krb5_dll(void)
	{
	HANDLE hKRB5_32;

	krb5_loaded++;
	hKRB5_32 = LoadLibrary(TEXT("KRB5_32"));
	if (!hKRB5_32)
		return;

	(FARPROC) p_krb5_free_data_contents =
		GetProcAddress( hKRB5_32, "krb5_free_data_contents" );
	(FARPROC) p_krb5_free_context =
		GetProcAddress( hKRB5_32, "krb5_free_context" );
	(FARPROC) p_krb5_auth_con_free =
		GetProcAddress( hKRB5_32, "krb5_auth_con_free" );
	(FARPROC) p_krb5_free_principal =
		GetProcAddress( hKRB5_32, "krb5_free_principal" );
	(FARPROC) p_krb5_mk_req_extended =
		GetProcAddress( hKRB5_32, "krb5_mk_req_extended" );
	(FARPROC) p_krb5_get_credentials =
		GetProcAddress( hKRB5_32, "krb5_get_credentials" );
	(FARPROC) p_krb5_cc_get_principal =
		GetProcAddress( hKRB5_32, "krb5_cc_get_principal" );
	(FARPROC) p_krb5_cc_default =
		GetProcAddress( hKRB5_32, "krb5_cc_default" );
	(FARPROC) p_krb5_sname_to_principal =
		GetProcAddress( hKRB5_32, "krb5_sname_to_principal" );
	(FARPROC) p_krb5_init_context =
		GetProcAddress( hKRB5_32, "krb5_init_context" );
	(FARPROC) p_krb5_free_ticket =
		GetProcAddress( hKRB5_32, "krb5_free_ticket" );
	(FARPROC) p_krb5_rd_req =
		GetProcAddress( hKRB5_32, "krb5_rd_req" );
	(FARPROC) p_krb5_principal_compare =
		GetProcAddress( hKRB5_32, "krb5_principal_compare" );
	(FARPROC) p_krb5_decrypt_tkt_part =
		GetProcAddress( hKRB5_32, "krb5_decrypt_tkt_part" );
	(FARPROC) p_krb5_timeofday =
		GetProcAddress( hKRB5_32, "krb5_timeofday" );
	(FARPROC) p_krb5_rc_default =
		GetProcAddress( hKRB5_32, "krb5_rc_default" );
	(FARPROC) p_krb5_rc_initialize =
		GetProcAddress( hKRB5_32, "krb5_rc_initialize" );
	(FARPROC) p_krb5_rc_get_lifespan =
		GetProcAddress( hKRB5_32, "krb5_rc_get_lifespan" );
	(FARPROC) p_krb5_rc_destroy =
		GetProcAddress( hKRB5_32, "krb5_rc_destroy" );
	(FARPROC) p_krb5_kt_default =
		GetProcAddress( hKRB5_32, "krb5_kt_default" );
	(FARPROC) p_krb5_kt_resolve =
		GetProcAddress( hKRB5_32, "krb5_kt_resolve" );
	(FARPROC) p_krb5_auth_con_init =
		GetProcAddress( hKRB5_32, "krb5_auth_con_init" );
        (FARPROC) p_valid_cksumtype =
                GetProcAddress( hKRB5_32, "valid_cksumtype" );
        (FARPROC) p_krb5_checksum_size =
                GetProcAddress( hKRB5_32, "krb5_checksum_size" );
        (FARPROC) p_krb5_kt_free_entry =
                GetProcAddress( hKRB5_32, "krb5_kt_free_entry" );
        (FARPROC) p_krb5_auth_con_setrcache =
                GetProcAddress( hKRB5_32, "krb5_auth_con_setrcache" );
        (FARPROC) p_krb5_get_server_rcache =
                GetProcAddress( hKRB5_32, "krb5_get_server_rcache" );
        (FARPROC) p_krb5_auth_con_getrcache =
                GetProcAddress( hKRB5_32, "krb5_auth_con_getrcache" );
        (FARPROC) p_krb5_kt_close =
                GetProcAddress( hKRB5_32, "krb5_kt_close" );
        (FARPROC) p_krb5_kt_get_entry =
                GetProcAddress( hKRB5_32, "krb5_kt_get_entry" );
	}


kssl_krb5_cc_get_principal
    (krb5_context context, krb5_ccache cache,
      krb5_principal *principal)
	{
	if ( p_krb5_cc_get_principal )
		return(p_krb5_cc_get_principal(context,cache,principal));
	else
		return(krb5_x
			((cache)->ops->get_princ,(context, cache, principal)));
	}


print_krb5_data(char *label, krb5_data *kdata)
        {
	int i;

	printf("%s[%d] ", label, kdata->length);
	for (i=0; i < (int)kdata->length; i++)
                {
		if (0 &&  isprint((int) kdata->data[i]))
                        printf(	"%c ",  kdata->data[i]);
		else
                        printf(	"%02x ", (unsigned char) kdata->data[i]);
		}
	printf("\n");
        }


kssl_krb5_free_data_contents(krb5_context CO, krb5_data  * data)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_free_data_contents )
		p_krb5_free_data_contents(CO,data);
	}


kssl_ctx_free(KSSL_CTX *kssl_ctx)
        {
	if (kssl_ctx == NULL)  return kssl_ctx;

	if (kssl_ctx->key)  		OPENSSL_cleanse(kssl_ctx->key,
							      kssl_ctx->length);
	if (kssl_ctx->key)  		kssl_free(kssl_ctx->key);
	if (kssl_ctx->client_princ) 	kssl_free(kssl_ctx->client_princ);
	if (kssl_ctx->service_host) 	kssl_free(kssl_ctx->service_host);
	if (kssl_ctx->service_name) 	kssl_free(kssl_ctx->service_name);
	if (kssl_ctx->keytab_file) 	kssl_free(kssl_ctx->keytab_file);

	kssl_free(kssl_ctx);
	return (KSSL_CTX *) NULL;
        }


kssl_krb5_cc_default(krb5_context CO,
                    krb5_ccache  * pCC)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_cc_default )
		return(p_krb5_cc_default(CO,pCC));
	else
		return KRB5KRB_ERR_GENERIC;
	}


kssl_ctx_new(void)
        {
	return ((KSSL_CTX *) kssl_calloc(1, sizeof(KSSL_CTX)));
        }


krb5_error_code  kssl_validate_times(	krb5_timestamp atime,
					krb5_ticket_times *ttimes)
	{
	krb5_deltat 	skew;
	krb5_timestamp	start, now;
	krb5_error_code	rc;
	krb5_context	context;

	if ((rc = krb5_init_context(&context)))	 return SSL_R_KRB5_S_BAD_TICKET;
	skew = get_rc_clockskew(context);
	if ((rc = krb5_timeofday(context,&now))) return SSL_R_KRB5_S_BAD_TICKET;
	krb5_free_context(context);

	if (atime  &&  labs(atime - now) >= skew)  return SSL_R_KRB5_S_TKT_SKEW;

	if (! ttimes)  return 0;

	start = (ttimes->starttime != 0)? ttimes->starttime: ttimes->authtime;
	if (start - now > skew)  return SSL_R_KRB5_S_TKT_NYV;
	if ((now - ttimes->endtime) > skew)  return SSL_R_KRB5_S_TKT_EXPIRED;

#ifdef KSSL_DEBUG
	printf("kssl_validate_times: %d |<-  | %d - %d | < %d  ->| %d\n",
		start, atime, now, skew, ttimes->endtime);
#endif	/* KSSL_DEBUG */

	return 0;
	}


void kssl_krb5_free_data_contents(krb5_context context, krb5_data *data)
	{
#ifdef KRB5_HEIMDAL
	data->length = 0;
        if (data->data)
            free(data->data);
#elif defined(KRB5_MIT_OLD11)
	if (data->data)  {
		krb5_xfree(data->data);
		data->data = 0;
		}
#else
	krb5_free_data_contents(NULL, data);
#endif
	}


kssl_krb5_auth_con_init(krb5_context CO,
                       krb5_auth_context  * pACO)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_auth_con_init )
		return(p_krb5_auth_con_init(CO,pACO));
	else
		return KRB5KRB_ERR_GENERIC;
	}


kssl_krb5_kt_resolve(krb5_context con,
                    krb5_const char * sz,
                    krb5_keytab * kt)
	{
	if (!krb5_loaded)
		load_krb5_dll();

	if ( p_krb5_kt_resolve )
		return(p_krb5_kt_resolve(con,sz,kt));
	else
		return KRB5KRB_ERR_GENERIC;
	}


sftp: Fix bug in sftp_mkdir not returning on error.

resolves: #84
(cherry picked from commit a92c97b2e17715c1b3cdd693d14af6c3311d8e44)

static void status_msg_free(sftp_status_message status){
  if (status == NULL) {
    return;
  }

  ssh_string_free(status->error);
  ssh_string_free(status->lang);
  SAFE_FREE(status->errormsg);
  SAFE_FREE(status->langmsg);
  SAFE_FREE(status);
}

Multiple double free vulnerabilities in the (1) agent_sign_data function in agent.c, (2) channel_request function in channels.c, (3) ssh_userauth_pubkey function in auth.c, (4) sftp_parse_attr_3 function in sftp.c, and (5) try_publickey_from_file function in keyfiles.c in libssh before 0.5.3 allow remote attackers to cause a denial of service (crash) and possibly execute arbitrary code via unspecified vectors.


int sftp_utimes(sftp_session sftp, const char *file,
    const struct timeval *times) {
	struct sftp_attributes_struct attr;
  ZERO_STRUCT(attr);

  attr.atime = times[0].tv_sec;
  attr.atime_nseconds = times[0].tv_usec;

  attr.mtime = times[1].tv_sec;
  attr.mtime_nseconds = times[1].tv_usec;

  attr.flags |= SSH_FILEXFER_ATTR_ACCESSTIME | SSH_FILEXFER_ATTR_MODIFYTIME |
    SSH_FILEXFER_ATTR_SUBSECOND_TIMES;

  return sftp_setstat(sftp, file, &attr);
}


uint64_t sftp_tell64(sftp_file file) {
  return (uint64_t) file->offset;
}


const char *sftp_extensions_get_name(sftp_session sftp, unsigned int idx) {
  if (sftp == NULL)
    return NULL;
  if (sftp->ext == NULL || sftp->ext->name == NULL) {
    ssh_set_error_invalid(sftp->session, __FUNCTION__);
    return NULL;
  }

  if (idx > sftp->ext->count) {
    ssh_set_error_invalid(sftp->session, __FUNCTION__);
    return NULL;
  }

  return sftp->ext->name[idx];
}


sftp_dir sftp_opendir(sftp_session sftp, const char *path){
  sftp_message msg = NULL;
  sftp_file file = NULL;
  sftp_dir dir = NULL;
  sftp_status_message status;
  ssh_string path_s;
  ssh_buffer payload;
  uint32_t id;

  payload = ssh_buffer_new();
  if (payload == NULL) {
    ssh_set_error_oom(sftp->session);
    return NULL;
  }

  path_s = ssh_string_from_char(path);
  if (path_s == NULL) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(payload);
    return NULL;
  }

  id = sftp_get_new_id(sftp);
  if (buffer_add_u32(payload, id) < 0 ||
      buffer_add_ssh_string(payload, path_s) < 0) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(payload);
    ssh_string_free(path_s);
    return NULL;
  }
  ssh_string_free(path_s);

  if (sftp_packet_write(sftp, SSH_FXP_OPENDIR, payload) < 0) {
    ssh_buffer_free(payload);
    return NULL;
  }
  ssh_buffer_free(payload);

  while (msg == NULL) {
    if (sftp_read_and_dispatch(sftp) < 0) {
      /* something nasty has happened */
      return NULL;
    }
    msg = sftp_dequeue(sftp, id);
  }

  switch (msg->packet_type) {
    case SSH_FXP_STATUS:
      status = parse_status_msg(msg);
      sftp_message_free(msg);
      if (status == NULL) {
        return NULL;
      }
      sftp_set_error(sftp, status->status);
      ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
          "SFTP server: %s", status->errormsg);
      status_msg_free(status);
      return NULL;
    case SSH_FXP_HANDLE:
      file = parse_handle_msg(msg);
      sftp_message_free(msg);
      if (file != NULL) {
        dir = malloc(sizeof(struct sftp_dir_struct));
        if (dir == NULL) {
          ssh_set_error_oom(sftp->session);
          return NULL;
        }
        ZERO_STRUCTP(dir);

        dir->sftp = sftp;
        dir->name = strdup(path);
        if (dir->name == NULL) {
          SAFE_FREE(dir);
          SAFE_FREE(file);
          return NULL;
        }
        dir->handle = file->handle;
        SAFE_FREE(file);
      }
      return dir;
    default:
      ssh_set_error(sftp->session, SSH_FATAL,
          "Received message %d during opendir!", msg->packet_type);
      sftp_message_free(msg);
  }

  return NULL;
}


sftp_session sftp_server_new(ssh_session session, ssh_channel chan){
  sftp_session sftp = NULL;

  sftp = malloc(sizeof(struct sftp_session_struct));
  if (sftp == NULL) {
    ssh_set_error_oom(session);
    return NULL;
  }
  ZERO_STRUCTP(sftp);

  sftp->session = session;
  sftp->channel = chan;

  return sftp;
}


static int sftp_enqueue(sftp_session sftp, sftp_message msg) {
  sftp_request_queue queue = NULL;
  sftp_request_queue ptr;

  queue = request_queue_new(msg);
  if (queue == NULL) {
    return -1;
  }

  ssh_log(sftp->session, SSH_LOG_PACKET,
      "Queued msg type %d id %d",
      msg->id, msg->packet_type);

  if(sftp->queue == NULL) {
    sftp->queue = queue;
  } else {
    ptr = sftp->queue;
    while(ptr->next) {
      ptr=ptr->next; /* find end of linked list */
    }
    ptr->next = queue; /* add it on bottom */
  }

  return 0;
}


int sftp_seek(sftp_file file, uint32_t new_offset) {
  if (file == NULL) {
    return -1;
  }

  file->offset = new_offset;
  file->eof = 0;

  return 0;
}


static sftp_ext sftp_ext_new(void) {
  sftp_ext ext;

  ext = malloc(sizeof(struct sftp_ext_struct));
  if (ext == NULL) {
    return NULL;
  }
  ZERO_STRUCTP(ext);

  return ext;
}


int sftp_dir_eof(sftp_dir dir) {
  return dir->eof;
}


int sftp_close(sftp_file file){
  int err = SSH_NO_ERROR;

  SAFE_FREE(file->name);
  if (file->handle){
    err = sftp_handle_close(file->sftp,file->handle);
    ssh_string_free(file->handle);
  }
  /* FIXME: check server response and implement errno */
  SAFE_FREE(file);

  return err;
}


int sftp_rmdir(sftp_session sftp, const char *directory) {
  sftp_status_message status = NULL;
  sftp_message msg = NULL;
  ssh_string filename;
  ssh_buffer buffer;
  uint32_t id;

  buffer = ssh_buffer_new();
  if (buffer == NULL) {
    ssh_set_error_oom(sftp->session);
    return -1;
  }

  filename = ssh_string_from_char(directory);
  if (filename == NULL) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    return -1;
  }

  id = sftp_get_new_id(sftp);
  if (buffer_add_u32(buffer, id) < 0 ||
      buffer_add_ssh_string(buffer, filename) < 0) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    ssh_string_free(filename);
    return -1;
  }
  if (sftp_packet_write(sftp, SSH_FXP_RMDIR, buffer) < 0) {
    ssh_buffer_free(buffer);
    ssh_string_free(filename);
    return -1;
  }
  ssh_buffer_free(buffer);
  ssh_string_free(filename);

  while (msg == NULL) {
    if (sftp_read_and_dispatch(sftp) < 0) {
      return -1;
    }
    msg = sftp_dequeue(sftp, id);
  }

  /* By specification, this command returns SSH_FXP_STATUS */
  if (msg->packet_type == SSH_FXP_STATUS) {
    status = parse_status_msg(msg);
    sftp_message_free(msg);
    if (status == NULL) {
      return -1;
    }
    sftp_set_error(sftp, status->status);
    switch (status->status) {
      case SSH_FX_OK:
        status_msg_free(status);
        return 0;
        break;
      default:
        break;
    }
    ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
        "SFTP server: %s", status->errormsg);
    status_msg_free(status);
    return -1;
  } else {
    ssh_set_error(sftp->session, SSH_FATAL,
        "Received message %d when attempting to remove directory",
        msg->packet_type);
    sftp_message_free(msg);
  }

  return -1;
}


void sftp_free(sftp_session sftp){
  sftp_request_queue ptr;

  if (sftp == NULL) {
    return;
  }

  ssh_channel_send_eof(sftp->channel);
  ptr = sftp->queue;
  while(ptr) {
    sftp_request_queue old;
    sftp_message_free(ptr->message);
    old = ptr->next;
    SAFE_FREE(ptr);
    ptr = old;
  }

  ssh_channel_free(sftp->channel);
  sftp_ext_free(sftp->ext);
  ZERO_STRUCTP(sftp);

  SAFE_FREE(sftp);
}


static sftp_file parse_handle_msg(sftp_message msg){
  sftp_file file;

  if(msg->packet_type != SSH_FXP_HANDLE) {
    ssh_set_error(msg->sftp->session, SSH_FATAL,
        "Not a ssh_fxp_handle message passed in!");
    return NULL;
  }

  file = malloc(sizeof(struct sftp_file_struct));
  if (file == NULL) {
    ssh_set_error_oom(msg->sftp->session);
    return NULL;
  }
  ZERO_STRUCTP(file);

  file->handle = buffer_get_ssh_string(msg->payload);
  if (file->handle == NULL) {
    ssh_set_error(msg->sftp->session, SSH_FATAL,
        "Invalid SSH_FXP_HANDLE message");
    SAFE_FREE(file);
    return NULL;
  }

  file->sftp = msg->sftp;
  file->offset = 0;
  file->eof = 0;

  return file;
}


char *sftp_canonicalize_path(sftp_session sftp, const char *path) {
  sftp_status_message status = NULL;
  sftp_message msg = NULL;
  ssh_string name = NULL;
  ssh_string pathstr;
  ssh_buffer buffer;
  char *cname;
  uint32_t ignored;
  uint32_t id;

  if (sftp == NULL)
    return NULL;
  if (path == NULL) {
    ssh_set_error_invalid(sftp->session, __FUNCTION__);
    return NULL;
  }

  buffer = ssh_buffer_new();
  if (buffer == NULL) {
    ssh_set_error_oom(sftp->session);
    return NULL;
  }

  pathstr = ssh_string_from_char(path);
  if (pathstr == NULL) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    return NULL;
  }

  id = sftp_get_new_id(sftp);
  if (buffer_add_u32(buffer, id) < 0 ||
      buffer_add_ssh_string(buffer, pathstr) < 0) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    ssh_string_free(pathstr);
    return NULL;
  }
  if (sftp_packet_write(sftp, SSH_FXP_REALPATH, buffer) < 0) {
    ssh_buffer_free(buffer);
    ssh_string_free(pathstr);
    return NULL;
  }
  ssh_buffer_free(buffer);
  ssh_string_free(pathstr);

  while (msg == NULL) {
    if (sftp_read_and_dispatch(sftp) < 0) {
      return NULL;
    }
    msg = sftp_dequeue(sftp, id);
  }

  if (msg->packet_type == SSH_FXP_NAME) {
    /* we don't care about "count" */
    buffer_get_u32(msg->payload, &ignored);
    /* we only care about the file name string */
    name = buffer_get_ssh_string(msg->payload);
    sftp_message_free(msg);
    if (name == NULL) {
      /* TODO: error message? */
      return NULL;
    }
    cname = ssh_string_to_char(name);
    ssh_string_free(name);
    if (cname == NULL) {
      ssh_set_error_oom(sftp->session);
    }
    return cname;
  } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */
    status = parse_status_msg(msg);
    sftp_message_free(msg);
    if (status == NULL) {
      return NULL;
    }
    ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
        "SFTP server: %s", status->errormsg);
    status_msg_free(status);
  } else { /* this shouldn't happen */
    ssh_set_error(sftp->session, SSH_FATAL,
        "Received message %d when attempting to set stats", msg->packet_type);
    sftp_message_free(msg);
  }

  return NULL;
}


static sftp_message sftp_dequeue(sftp_session sftp, uint32_t id){
  sftp_request_queue prev = NULL;
  sftp_request_queue queue;
  sftp_message msg;

  if(sftp->queue == NULL) {
    return NULL;
  }

  queue = sftp->queue;
  while (queue) {
    if(queue->message->id == id) {
      /* remove from queue */
      if (prev == NULL) {
        sftp->queue = queue->next;
      } else {
        prev->next = queue->next;
      }
      msg = queue->message;
      request_queue_free(queue);
      ssh_log(sftp->session, SSH_LOG_PACKET,
          "Dequeued msg id %d type %d",
          msg->id,
          msg->packet_type);
      return msg;
    }
    prev = queue;
    queue = queue->next;
  }

  return NULL;
}


unsigned long sftp_tell(sftp_file file) {
  return (unsigned long)file->offset;
}


int sftp_server_version(sftp_session sftp) {
  return sftp->server_version;
}


static int sftp_handle_close(sftp_session sftp, ssh_string handle) {
  sftp_status_message status;
  sftp_message msg = NULL;
  ssh_buffer buffer = NULL;
  uint32_t id;

  buffer = ssh_buffer_new();
  if (buffer == NULL) {
    ssh_set_error_oom(sftp->session);
    return -1;
  }

  id = sftp_get_new_id(sftp);
  if (buffer_add_u32(buffer, id) < 0 ||
      buffer_add_ssh_string(buffer, handle) < 0) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    return -1;
  }
  if (sftp_packet_write(sftp, SSH_FXP_CLOSE ,buffer) < 0) {
    ssh_buffer_free(buffer);
    return -1;
  }
  ssh_buffer_free(buffer);

  while (msg == NULL) {
    if (sftp_read_and_dispatch(sftp) < 0) {
      /* something nasty has happened */
      return -1;
    }
    msg = sftp_dequeue(sftp,id);
  }

  switch (msg->packet_type) {
    case SSH_FXP_STATUS:
      status = parse_status_msg(msg);
      sftp_message_free(msg);
      if(status == NULL) {
        return -1;
      }
      sftp_set_error(sftp, status->status);
      switch (status->status) {
        case SSH_FX_OK:
          status_msg_free(status);
          return 0;
          break;
        default:
          break;
      }
      ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
          "SFTP server: %s", status->errormsg);
      status_msg_free(status);
      return -1;
    default:
      ssh_set_error(sftp->session, SSH_FATAL,
          "Received message %d during sftp_handle_close!", msg->packet_type);
      sftp_message_free(msg);
  }

  return -1;
}


sftp_attributes sftp_lstat(sftp_session session, const char *path) {
  return sftp_xstat(session, path, SSH_FXP_LSTAT);
}


static sftp_request_queue request_queue_new(sftp_message msg) {
  sftp_request_queue queue = NULL;

  queue = malloc(sizeof(struct sftp_request_queue_struct));
  if (queue == NULL) {
    ssh_set_error_oom(msg->sftp->session);
    return NULL;
  }
  ZERO_STRUCTP(queue);

  queue->message = msg;

  return queue;
}


static void sftp_message_free(sftp_message msg) {
  sftp_session sftp;

  if (msg == NULL) {
    return;
  }

  sftp = msg->sftp;
  sftp_enter_function();

  ssh_buffer_free(msg->payload);
  SAFE_FREE(msg);

  sftp_leave_function();
}


static char *sftp_parse_longname(const char *longname,
        enum sftp_longname_field_e longname_field) {
    const char *p, *q;
    size_t len, field = 0;
    char *x;

    p = longname;
    /* Find the beginning of the field which is specified by sftp_longanme_field_e. */
    while(field != longname_field) {
        if(isspace(*p)) {
            field++;
            p++;
            while(*p && isspace(*p)) {
                p++;
            }
        } else {
            p++;
        }
    }

    q = p;
    while (! isspace(*q)) {
        q++;
    }

    /* There is no strndup on windows */
    len = q - p + 1;
    x = malloc(len);
    if (x == NULL) {
      return NULL;
    }

    snprintf(x, len, "%s", p);

    return x;
}


char *sftp_readlink(sftp_session sftp, const char *path) {
  sftp_status_message status = NULL;
  sftp_message msg = NULL;
  ssh_string path_s = NULL;
  ssh_string link_s = NULL;
  ssh_buffer buffer;
  char *lnk;
  uint32_t ignored;
  uint32_t id;

  if (sftp == NULL)
    return NULL;
  if (path == NULL) {
    ssh_set_error_invalid(sftp, __FUNCTION__);
    return NULL;
  }
  if (sftp->version < 3){
    ssh_set_error(sftp,SSH_REQUEST_DENIED,"sftp version %d does not support sftp_readlink",sftp->version);
    return NULL;
  }
  buffer = ssh_buffer_new();
  if (buffer == NULL) {
    ssh_set_error_oom(sftp->session);
    return NULL;
  }

  path_s = ssh_string_from_char(path);
  if (path_s == NULL) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    return NULL;
  }

  id = sftp_get_new_id(sftp);
  if (buffer_add_u32(buffer, id) < 0 ||
      buffer_add_ssh_string(buffer, path_s) < 0) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    ssh_string_free(path_s);
    return NULL;
  }
  if (sftp_packet_write(sftp, SSH_FXP_READLINK, buffer) < 0) {
    ssh_buffer_free(buffer);
    ssh_string_free(path_s);
    return NULL;
  }
  ssh_buffer_free(buffer);
  ssh_string_free(path_s);

  while (msg == NULL) {
    if (sftp_read_and_dispatch(sftp) < 0) {
      return NULL;
    }
    msg = sftp_dequeue(sftp, id);
  }

  if (msg->packet_type == SSH_FXP_NAME) {
    /* we don't care about "count" */
    buffer_get_u32(msg->payload, &ignored);
    /* we only care about the file name string */
    link_s = buffer_get_ssh_string(msg->payload);
    sftp_message_free(msg);
    if (link_s == NULL) {
      /* TODO: what error to set here? */
      return NULL;
    }
    lnk = ssh_string_to_char(link_s);
    ssh_string_free(link_s);

    return lnk;
  } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */
    status = parse_status_msg(msg);
    sftp_message_free(msg);
    if (status == NULL) {
      return NULL;
    }
    ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
        "SFTP server: %s", status->errormsg);
    status_msg_free(status);
  } else { /* this shouldn't happen */
    ssh_set_error(sftp->session, SSH_FATAL,
        "Received message %d when attempting to set stats", msg->packet_type);
    sftp_message_free(msg);
  }

  return NULL;
}


int sftp_closedir(sftp_dir dir){
  int err = SSH_NO_ERROR;

  SAFE_FREE(dir->name);
  if (dir->handle) {
    err = sftp_handle_close(dir->sftp, dir->handle);
    ssh_string_free(dir->handle);
  }
  /* FIXME: check server response and implement errno */
  ssh_buffer_free(dir->buffer);
  SAFE_FREE(dir);

  return err;
}


void sftp_rewind(sftp_file file) {
  file->offset = 0;
  file->eof = 0;
}


int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload){
  int size;

  if (buffer_prepend_data(payload, &type, sizeof(uint8_t)) < 0) {
    ssh_set_error_oom(sftp->session);
    return -1;
  }

  size = htonl(buffer_get_rest_len(payload));
  if (buffer_prepend_data(payload, &size, sizeof(uint32_t)) < 0) {
    ssh_set_error_oom(sftp->session);
    return -1;
  }

  size = ssh_channel_write(sftp->channel, buffer_get_rest(payload),
      buffer_get_rest_len(payload));
  if (size < 0) {
    return -1;
  } else if((uint32_t) size != buffer_get_rest_len(payload)) {
    ssh_log(sftp->session, SSH_LOG_PACKET,
        "Had to write %d bytes, wrote only %d",
        buffer_get_rest_len(payload),
        size);
  }

  return size;
}


void sftp_statvfs_free(sftp_statvfs_t statvfs) {
  if (statvfs == NULL) {
    return;
  }

  SAFE_FREE(statvfs);
}


void sftp_packet_free(sftp_packet packet) {
  if (packet == NULL) {
    return;
  }

  ssh_buffer_free(packet->payload);
  free(packet);
}


void sftp_file_set_nonblocking(sftp_file handle){
    handle->nonblocking=1;
}


int sftp_server_init(sftp_session sftp){
  ssh_session session = sftp->session;
  sftp_packet packet = NULL;
  ssh_buffer reply = NULL;
  uint32_t version;

  sftp_enter_function();

  packet = sftp_packet_read(sftp);
  if (packet == NULL) {
    sftp_leave_function();
    return -1;
  }

  if (packet->type != SSH_FXP_INIT) {
    ssh_set_error(session, SSH_FATAL,
        "Packet read of type %d instead of SSH_FXP_INIT",
        packet->type);

    sftp_packet_free(packet);
    sftp_leave_function();
    return -1;
  }

  ssh_log(session, SSH_LOG_PACKET, "Received SSH_FXP_INIT");

  buffer_get_u32(packet->payload, &version);
  version = ntohl(version);
  ssh_log(session, SSH_LOG_PACKET, "Client version: %d", version);
  sftp->client_version = version;

  sftp_packet_free(packet);

  reply = ssh_buffer_new();
  if (reply == NULL) {
    ssh_set_error_oom(session);
    sftp_leave_function();
    return -1;
  }

  if (buffer_add_u32(reply, ntohl(LIBSFTP_VERSION)) < 0) {
    ssh_set_error_oom(session);
    ssh_buffer_free(reply);
    sftp_leave_function();
    return -1;
  }

  if (sftp_packet_write(sftp, SSH_FXP_VERSION, reply) < 0) {
    ssh_buffer_free(reply);
    sftp_leave_function();
    return -1;
  }
  ssh_buffer_free(reply);

  ssh_log(session, SSH_LOG_RARE, "Server version sent");

  if (version > LIBSFTP_VERSION) {
    sftp->version = LIBSFTP_VERSION;
  } else {
    sftp->version=version;
  }

  sftp_leave_function();
  return 0;
}


static void sftp_set_error(sftp_session sftp, int errnum) {
  if (sftp != NULL) {
    sftp->errnum = errnum;
  }
}


int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode) {
  sftp_status_message status = NULL;
  sftp_message msg = NULL;
  sftp_attributes errno_attr = NULL;
  struct sftp_attributes_struct attr;
  ssh_buffer buffer;
  ssh_string path;
  uint32_t id;

  buffer = ssh_buffer_new();
  if (buffer == NULL) {
    ssh_set_error_oom(sftp->session);
    return -1;
  }

  path = ssh_string_from_char(directory);
  if (path == NULL) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    return -1;
  }

  ZERO_STRUCT(attr);
  attr.permissions = mode;
  attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS;

  id = sftp_get_new_id(sftp);
  if (buffer_add_u32(buffer, id) < 0 ||
      buffer_add_ssh_string(buffer, path) < 0 ||
      buffer_add_attributes(buffer, &attr) < 0 ||
      sftp_packet_write(sftp, SSH_FXP_MKDIR, buffer) < 0) {
    ssh_buffer_free(buffer);
    ssh_string_free(path);
    return -1;
  }
  ssh_buffer_free(buffer);
  ssh_string_free(path);

  while (msg == NULL) {
    if (sftp_read_and_dispatch(sftp) < 0) {
      return -1;
    }
    msg = sftp_dequeue(sftp, id);
  }

  /* By specification, this command only returns SSH_FXP_STATUS */
  if (msg->packet_type == SSH_FXP_STATUS) {
    status = parse_status_msg(msg);
    sftp_message_free(msg);
    if (status == NULL) {
      return -1;
    }
    sftp_set_error(sftp, status->status);
    switch (status->status) {
      case SSH_FX_FAILURE:
        /*
         * mkdir always returns a failure, even if the path already exists.
         * To be POSIX conform and to be able to map it to EEXIST a stat
         * call is needed here.
         */
        errno_attr = sftp_lstat(sftp, directory);
        if (errno_attr != NULL) {
          SAFE_FREE(errno_attr);
          sftp_set_error(sftp, SSH_FX_FILE_ALREADY_EXISTS);
        }
        break;
      case SSH_FX_OK:
        status_msg_free(status);
        return 0;
        break;
      default:
        break;
    }
    /*
     * The status should be SSH_FX_OK if the command was successful, if it
     * didn't, then there was an error
     */
    ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
        "SFTP server: %s", status->errormsg);
    status_msg_free(status);
    return -1;
  } else {
    ssh_set_error(sftp->session, SSH_FATAL,
        "Received message %d when attempting to make directory",
        msg->packet_type);
    sftp_message_free(msg);
  }

  return -1;
}


static sftp_message sftp_message_new(sftp_session sftp){
  sftp_message msg = NULL;

  sftp_enter_function();

  msg = malloc(sizeof(struct sftp_message_struct));
  if (msg == NULL) {
    ssh_set_error_oom(sftp->session);
    return NULL;
  }
  ZERO_STRUCTP(msg);

  msg->payload = ssh_buffer_new();
  if (msg->payload == NULL) {
    ssh_set_error_oom(sftp->session);
    SAFE_FREE(msg);
    return NULL;
  }
  msg->sftp = sftp;

  sftp_leave_function();
  return msg;
}


void sftp_file_set_blocking(sftp_file handle){
    handle->nonblocking=0;
}


int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) {
  uint32_t id;
  ssh_buffer buffer;
  ssh_string path;
  sftp_message msg = NULL;
  sftp_status_message status = NULL;

  buffer = ssh_buffer_new();
  if (buffer == NULL) {
    ssh_set_error_oom(sftp->session);
    return -1;
  }

  path = ssh_string_from_char(file);
  if (path == NULL) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    return -1;
  }

  id = sftp_get_new_id(sftp);
  if (buffer_add_u32(buffer, id) < 0 ||
      buffer_add_ssh_string(buffer, path) < 0 ||
      buffer_add_attributes(buffer, attr) < 0) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    ssh_string_free(path);
    return -1;
  }
  if (sftp_packet_write(sftp, SSH_FXP_SETSTAT, buffer) < 0) {
    ssh_buffer_free(buffer);
    ssh_string_free(path);
    return -1;
  }
  ssh_buffer_free(buffer);
  ssh_string_free(path);

  while (msg == NULL) {
    if (sftp_read_and_dispatch(sftp) < 0) {
      return -1;
    }
    msg = sftp_dequeue(sftp, id);
  }

  /* By specification, this command only returns SSH_FXP_STATUS */
  if (msg->packet_type == SSH_FXP_STATUS) {
    status = parse_status_msg(msg);
    sftp_message_free(msg);
    if (status == NULL) {
      return -1;
    }
    sftp_set_error(sftp, status->status);
    switch (status->status) {
      case SSH_FX_OK:
        status_msg_free(status);
        return 0;
      default:
        break;
    }
    /*
     * The status should be SSH_FX_OK if the command was successful, if it
     * didn't, then there was an error
     */
    ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
        "SFTP server: %s", status->errormsg);
    status_msg_free(status);
    return -1;
  } else {
    ssh_set_error(sftp->session, SSH_FATAL,
        "Received message %d when attempting to set stats", msg->packet_type);
    sftp_message_free(msg);
  }

  return -1;
}


int sftp_seek64(sftp_file file, uint64_t new_offset) {
  if (file == NULL) {
    return -1;
  }

  file->offset = new_offset;
  file->eof = 0;

  return 0;
}


static sftp_attributes sftp_parse_attr_4(sftp_session sftp, ssh_buffer buf,
    int expectnames) {
  sftp_attributes attr;
  ssh_string owner = NULL;
  ssh_string group = NULL;
  uint32_t flags = 0;
  int ok = 0;

  /* unused member variable */
  (void) expectnames;

  attr = malloc(sizeof(struct sftp_attributes_struct));
  if (attr == NULL) {
    ssh_set_error_oom(sftp->session);
    return NULL;
  }
  ZERO_STRUCTP(attr);

  /* This isn't really a loop, but it is like a try..catch.. */
  do {
    if (buffer_get_u32(buf, &flags) != 4) {
      break;
    }

    flags = ntohl(flags);
    attr->flags = flags;

    if (flags & SSH_FILEXFER_ATTR_SIZE) {
      if (buffer_get_u64(buf, &attr->size) != 8) {
        break;
      }
      attr->size = ntohll(attr->size);
    }

    if (flags & SSH_FILEXFER_ATTR_OWNERGROUP) {
      if((owner = buffer_get_ssh_string(buf)) == NULL ||
        (attr->owner = ssh_string_to_char(owner)) == NULL) {
        break;
      }
      if ((group = buffer_get_ssh_string(buf)) == NULL ||
        (attr->group = ssh_string_to_char(group)) == NULL) {
        break;
      }
    }

    if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
      if (buffer_get_u32(buf, &attr->permissions) != 4) {
        break;
      }
      attr->permissions = ntohl(attr->permissions);

      /* FIXME on windows! */
      switch (attr->permissions & S_IFMT) {
        case S_IFSOCK:
        case S_IFBLK:
        case S_IFCHR:
        case S_IFIFO:
          attr->type = SSH_FILEXFER_TYPE_SPECIAL;
          break;
        case S_IFLNK:
          attr->type = SSH_FILEXFER_TYPE_SYMLINK;
          break;
        case S_IFREG:
          attr->type = SSH_FILEXFER_TYPE_REGULAR;
          break;
        case S_IFDIR:
          attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
          break;
        default:
          attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
          break;
      }
    }

    if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) {
      if (buffer_get_u64(buf, &attr->atime64) != 8) {
        break;
      }
      attr->atime64 = ntohll(attr->atime64);
    }

    if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
      if (buffer_get_u32(buf, &attr->atime_nseconds) != 4) {
        break;
      }
      attr->atime_nseconds = ntohl(attr->atime_nseconds);
    }

    if (flags & SSH_FILEXFER_ATTR_CREATETIME) {
      if (buffer_get_u64(buf, &attr->createtime) != 8) {
        break;
      }
      attr->createtime = ntohll(attr->createtime);
    }

    if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
      if (buffer_get_u32(buf, &attr->createtime_nseconds) != 4) {
        break;
      }
      attr->createtime_nseconds = ntohl(attr->createtime_nseconds);
    }

    if (flags & SSH_FILEXFER_ATTR_MODIFYTIME) {
      if (buffer_get_u64(buf, &attr->mtime64) != 8) {
        break;
      }
      attr->mtime64 = ntohll(attr->mtime64);
    }

    if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) {
      if (buffer_get_u32(buf, &attr->mtime_nseconds) != 4) {
        break;
      }
      attr->mtime_nseconds = ntohl(attr->mtime_nseconds);
    }

    if (flags & SSH_FILEXFER_ATTR_ACL) {
      if ((attr->acl = buffer_get_ssh_string(buf)) == NULL) {
        break;
      }
    }

    if (flags & SSH_FILEXFER_ATTR_EXTENDED) {
      if (buffer_get_u32(buf,&attr->extended_count) != 4) {
        break;
      }
      attr->extended_count = ntohl(attr->extended_count);

      while(attr->extended_count &&
          (attr->extended_type = buffer_get_ssh_string(buf)) &&
          (attr->extended_data = buffer_get_ssh_string(buf))){
        attr->extended_count--;
      }

      if (attr->extended_count) {
        break;
      }
    }
    ok = 1;
  } while (0);

  if (ok == 0) {
    /* break issued somewhere */
    ssh_string_free(owner);
    ssh_string_free(group);
    ssh_string_free(attr->acl);
    ssh_string_free(attr->extended_type);
    ssh_string_free(attr->extended_data);
    SAFE_FREE(attr->owner);
    SAFE_FREE(attr->group);
    SAFE_FREE(attr);

    ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");

    return NULL;
  }

  return attr;
}


int sftp_rename(sftp_session sftp, const char *original, const char *newname) {
  sftp_status_message status = NULL;
  sftp_message msg = NULL;
  ssh_buffer buffer;
  ssh_string oldpath;
  ssh_string newpath;
  uint32_t id;

  buffer = ssh_buffer_new();
  if (buffer == NULL) {
    ssh_set_error_oom(sftp->session);
    return -1;
  }

  oldpath = ssh_string_from_char(original);
  if (oldpath == NULL) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    return -1;
  }

  newpath = ssh_string_from_char(newname);
  if (newpath == NULL) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    ssh_string_free(oldpath);
    return -1;
  }

  id = sftp_get_new_id(sftp);
  if (buffer_add_u32(buffer, id) < 0 ||
      buffer_add_ssh_string(buffer, oldpath) < 0 ||
      buffer_add_ssh_string(buffer, newpath) < 0 ||
      /* POSIX rename atomically replaces newpath, we should do the same
       * only available on >=v4 */
      sftp->version>=4 ? (buffer_add_u32(buffer, SSH_FXF_RENAME_OVERWRITE) < 0):0) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    ssh_string_free(oldpath);
    ssh_string_free(newpath);
    return -1;
  }
  if (sftp_packet_write(sftp, SSH_FXP_RENAME, buffer) < 0) {
    ssh_buffer_free(buffer);
    ssh_string_free(oldpath);
    ssh_string_free(newpath);
    return -1;
  }
  ssh_buffer_free(buffer);
  ssh_string_free(oldpath);
  ssh_string_free(newpath);

  while (msg == NULL) {
    if (sftp_read_and_dispatch(sftp) < 0) {
      return -1;
    }
    msg = sftp_dequeue(sftp, id);
  }

  /* By specification, this command only returns SSH_FXP_STATUS */
  if (msg->packet_type == SSH_FXP_STATUS) {
    status = parse_status_msg(msg);
    sftp_message_free(msg);
    if (status == NULL) {
      return -1;
    }
    sftp_set_error(sftp, status->status);
    switch (status->status) {
      case SSH_FX_OK:
        status_msg_free(status);
        return 0;
      default:
        break;
    }
    /*
     * Status should be SSH_FX_OK if the command was successful, if it didn't,
     * then there was an error
     */
    ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
        "SFTP server: %s", status->errormsg);
    status_msg_free(status);
    return -1;
  } else {
    ssh_set_error(sftp->session, SSH_FATAL,
        "Received message %d when attempting to rename",
        msg->packet_type);
    sftp_message_free(msg);
  }

  return -1;
}


static sftp_statvfs_t sftp_parse_statvfs(sftp_session sftp, ssh_buffer buf) {
	sftp_statvfs_t  statvfs;
  uint64_t tmp;
  int ok = 0;

  statvfs = malloc(sizeof(struct sftp_statvfs_struct));
  if (statvfs == NULL) {
    ssh_set_error_oom(sftp->session);
    return NULL;
  }
  ZERO_STRUCTP(statvfs);

  /* try .. catch */
  do {
    /* file system block size */
    if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
      break;
    }
    statvfs->f_bsize = ntohll(tmp);

    /* fundamental fs block size */
    if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
      break;
    }
    statvfs->f_frsize = ntohll(tmp);

    /* number of blocks (unit f_frsize) */
    if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
      break;
    }
    statvfs->f_blocks = ntohll(tmp);

    /* free blocks in file system */
    if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
      break;
    }
    statvfs->f_bfree = ntohll(tmp);

    /* free blocks for non-root */
    if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
      break;
    }
    statvfs->f_bavail = ntohll(tmp);

    /* total file inodes */
    if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
      break;
    }
    statvfs->f_files = ntohll(tmp);

    /* free file inodes */
    if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
      break;
    }
    statvfs->f_ffree = ntohll(tmp);

    /* free file inodes for to non-root */
    if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
      break;
    }
    statvfs->f_favail = ntohll(tmp);

    /* file system id */
    if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
      break;
    }
    statvfs->f_fsid = ntohll(tmp);

    /* bit mask of f_flag values */
    if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
      break;
    }
    statvfs->f_flag = ntohll(tmp);

    /* maximum filename length */
    if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) {
      break;
    }
    statvfs->f_namemax = ntohll(tmp);

    ok = 1;
  } while(0);

  if (!ok) {
    SAFE_FREE(statvfs);
    ssh_set_error(sftp->session, SSH_FATAL, "Invalid statvfs structure");
    return NULL;
  }

  return statvfs;
}


static sftp_attributes sftp_xstat(sftp_session sftp, const char *path,
    int param) {
  sftp_status_message status = NULL;
  sftp_message msg = NULL;
  ssh_string pathstr;
  ssh_buffer buffer;
  uint32_t id;

  buffer = ssh_buffer_new();
  if (buffer == NULL) {
    ssh_set_error_oom(sftp->session);
    return NULL;
  }

  pathstr = ssh_string_from_char(path);
  if (pathstr == NULL) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    return NULL;
  }

  id = sftp_get_new_id(sftp);
  if (buffer_add_u32(buffer, id) < 0 ||
      buffer_add_ssh_string(buffer, pathstr) < 0) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    ssh_string_free(pathstr);
    return NULL;
  }
  if (sftp_packet_write(sftp, param, buffer) < 0) {
    ssh_buffer_free(buffer);
    ssh_string_free(pathstr);
    return NULL;
  }
  ssh_buffer_free(buffer);
  ssh_string_free(pathstr);

  while (msg == NULL) {
    if (sftp_read_and_dispatch(sftp) < 0) {
      return NULL;
    }
    msg = sftp_dequeue(sftp, id);
  }

  if (msg->packet_type == SSH_FXP_ATTRS) {
    sftp_attributes attr = sftp_parse_attr(sftp, msg->payload, 0);
    sftp_message_free(msg);

    return attr;
  } else if (msg->packet_type == SSH_FXP_STATUS) {
    status = parse_status_msg(msg);
    sftp_message_free(msg);
    if (status == NULL) {
      return NULL;
    }
    sftp_set_error(sftp, status->status);
    ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
        "SFTP server: %s", status->errormsg);
    status_msg_free(status);
    return NULL;
  }
  ssh_set_error(sftp->session, SSH_FATAL,
      "Received mesg %d during stat()", msg->packet_type);
  sftp_message_free(msg);

  return NULL;
}


int sftp_async_read(sftp_file file, void *data, uint32_t size, uint32_t id){
  sftp_session sftp = file->sftp;
  sftp_message msg = NULL;
  sftp_status_message status;
  ssh_string datastring;
  int err = SSH_OK;
  uint32_t len;

  sftp_enter_function();

  if (file->eof) {
    sftp_leave_function();
    return 0;
  }

  /* handle an existing request */
  while (msg == NULL) {
    if (file->nonblocking){
      if (ssh_channel_poll(sftp->channel, 0) == 0) {
        /* we cannot block */
        return SSH_AGAIN;
      }
    }

    if (sftp_read_and_dispatch(sftp) < 0) {
      /* something nasty has happened */
      sftp_leave_function();
      return SSH_ERROR;
    }

    msg = sftp_dequeue(sftp,id);
  }

  switch (msg->packet_type) {
    case SSH_FXP_STATUS:
      status = parse_status_msg(msg);
      sftp_message_free(msg);
      if (status == NULL) {
        sftp_leave_function();
        return -1;
      }
      sftp_set_error(sftp, status->status);
      if (status->status != SSH_FX_EOF) {
        ssh_set_error(sftp->session, SSH_REQUEST_DENIED,
            "SFTP server : %s", status->errormsg);
        sftp_leave_function();
        err = SSH_ERROR;
      } else {
        file->eof = 1;
      }
      status_msg_free(status);
      sftp_leave_function();
      return err;
    case SSH_FXP_DATA:
      datastring = buffer_get_ssh_string(msg->payload);
      sftp_message_free(msg);
      if (datastring == NULL) {
        ssh_set_error(sftp->session, SSH_FATAL,
            "Received invalid DATA packet from sftp server");
        sftp_leave_function();
        return SSH_ERROR;
      }
      if (ssh_string_len(datastring) > size) {
        ssh_set_error(sftp->session, SSH_FATAL,
            "Received a too big DATA packet from sftp server: "
            "%" PRIdS " and asked for %u",
            ssh_string_len(datastring), size);
        ssh_string_free(datastring);
        sftp_leave_function();
        return SSH_ERROR;
      }
      len = ssh_string_len(datastring);
      /* Update the offset with the correct value */
      file->offset = file->offset - (size - len);
      memcpy(data, ssh_string_data(datastring), len);
      ssh_string_free(datastring);
      sftp_leave_function();
      return len;
    default:
      ssh_set_error(sftp->session,SSH_FATAL,"Received message %d during read!",msg->packet_type);
      sftp_message_free(msg);
      sftp_leave_function();
      return SSH_ERROR;
  }

  sftp_leave_function();
  return SSH_ERROR;
}


ssize_t sftp_read(sftp_file handle, void *buf, size_t count) {
  sftp_session sftp = handle->sftp;
  sftp_message msg = NULL;
  sftp_status_message status;
  ssh_string datastring;
  ssh_buffer buffer;
  int id;

  if (handle->eof) {
    return 0;
  }

  buffer = ssh_buffer_new();
  if (buffer == NULL) {
    ssh_set_error_oom(sftp->session);
    return -1;
  }
  id = sftp_get_new_id(handle->sftp);
  if (buffer_add_u32(buffer, id) < 0 ||
      buffer_add_ssh_string(buffer, handle->handle) < 0 ||
      buffer_add_u64(buffer, htonll(handle->offset)) < 0 ||
      buffer_add_u32(buffer,htonl(count)) < 0) {
    ssh_set_error_oom(sftp->session);
    ssh_buffer_free(buffer);
    return -1;
  }
  if (sftp_packet_write(handle->sftp, SSH_FXP_READ, buffer) < 0) {
    ssh_buffer_free(buffer);
    return -1;
  }
  ssh_buffer_free(buffer);

  while (msg == NULL) {
    if (handle->nonblocking) {
      if (ssh_channel_poll(handle->sftp->channel, 0) == 0) {
        /* we cannot block */
        return 0;
      }
    }
    if (sftp_read_and_dispatch(handle->sftp) < 0) {
      /* something nasty has happened */
      return -1;
    }
    msg = sftp_dequeue(handle->sftp, id);
  }

  switch (msg->packet_type) {
    case SSH_FXP_STATUS:
      status = parse_status_msg(msg);
      sftp_message_free(msg);
      if (status == NULL) {
        return -1;
      }
      sftp_set_error(sftp, status->status);
      switch (status->status) {
        case SSH_FX_EOF:
          handle->eof = 1;
          status_msg_free(status);
          return 0;
        default:
          break;
      }
      ssh_set_error(sftp->session,SSH_REQUEST_DENIED,
          "SFTP server: %s", status->errormsg);
      status_msg_free(status);
      return -1;
    case SSH_FXP_DATA:
      datastring = buffer_get_ssh_string(msg->payload);
      sftp_message_free(msg);
      if (datastring == NULL) {
        ssh_set_error(sftp->session, SSH_FATAL,
            "Received invalid DATA packet from sftp server");
        return -1;
      }

      if (ssh_string_len(datastring) > count) {
        ssh_set_error(sftp->session, SSH_FATAL,
            "Received a too big DATA packet from sftp server: "
            "%" PRIdS " and asked for %" PRIdS,
            ssh_string_len(datastring), count);
        ssh_string_free(datastring);
        return -1;
      }
      count = ssh_string_len(datastring);
      handle->offset += count;
      memcpy(buf, ssh_string_data(datastring), count);
      ssh_string_free(datastring);
      return count;
    default:
      ssh_set_error(sftp->session, SSH_FATAL,
          "Received message %d during read!", msg->packet_type);
      sftp_message_free(msg);
      return -1;
  }

  return -1; /* not reached */
}


static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf,
    int expectname) {
  ssh_string longname = NULL;
  ssh_string name = NULL;
  sftp_attributes attr;
  uint32_t flags = 0;
  int ok = 0;

  attr = malloc(sizeof(struct sftp_attributes_struct));
  if (attr == NULL) {
    ssh_set_error_oom(sftp->session);
    return NULL;
  }
  ZERO_STRUCTP(attr);

  /* This isn't really a loop, but it is like a try..catch.. */
  do {
    if (expectname) {
      if ((name = buffer_get_ssh_string(buf)) == NULL ||
          (attr->name = ssh_string_to_char(name)) == NULL) {
        break;
      }
      ssh_string_free(name);

      ssh_log(sftp->session, SSH_LOG_RARE, "Name: %s", attr->name);

      if ((longname=buffer_get_ssh_string(buf)) == NULL ||
          (attr->longname=ssh_string_to_char(longname)) == NULL) {
        break;
      }
      ssh_string_free(longname);

      /* Set owner and group if we talk to openssh and have the longname */
      if (ssh_get_openssh_version(sftp->session)) {
        attr->owner = sftp_parse_longname(attr->longname, SFTP_LONGNAME_OWNER);
        if (attr->owner == NULL) {
          break;
        }

        attr->group = sftp_parse_longname(attr->longname, SFTP_LONGNAME_GROUP);
        if (attr->group == NULL) {
          break;
        }
      }
    }

    if (buffer_get_u32(buf, &flags) != sizeof(uint32_t)) {
      break;
    }
    flags = ntohl(flags);
    attr->flags = flags;
    ssh_log(sftp->session, SSH_LOG_RARE,
        "Flags: %.8lx\n", (long unsigned int) flags);

    if (flags & SSH_FILEXFER_ATTR_SIZE) {
      if(buffer_get_u64(buf, &attr->size) != sizeof(uint64_t)) {
        break;
      }
      attr->size = ntohll(attr->size);
      ssh_log(sftp->session, SSH_LOG_RARE,
          "Size: %llu\n",
          (long long unsigned int) attr->size);
    }

    if (flags & SSH_FILEXFER_ATTR_UIDGID) {
      if (buffer_get_u32(buf, &attr->uid) != sizeof(uint32_t)) {
        break;
      }
      if (buffer_get_u32(buf, &attr->gid) != sizeof(uint32_t)) {
        break;
      }
      attr->uid = ntohl(attr->uid);
      attr->gid = ntohl(attr->gid);
    }

    if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
      if (buffer_get_u32(buf, &attr->permissions) != sizeof(uint32_t)) {
        break;
      }
      attr->permissions = ntohl(attr->permissions);

      switch (attr->permissions & S_IFMT) {
        case S_IFSOCK:
        case S_IFBLK:
        case S_IFCHR:
        case S_IFIFO:
          attr->type = SSH_FILEXFER_TYPE_SPECIAL;
          break;
        case S_IFLNK:
          attr->type = SSH_FILEXFER_TYPE_SYMLINK;
          break;
        case S_IFREG:
          attr->type = SSH_FILEXFER_TYPE_REGULAR;
          break;
        case S_IFDIR:
          attr->type = SSH_FILEXFER_TYPE_DIRECTORY;
          break;
        default:
          attr->type = SSH_FILEXFER_TYPE_UNKNOWN;
          break;
      }
    }

    if (flags & SSH_FILEXFER_ATTR_ACMODTIME) {
      if (buffer_get_u32(buf, &attr->atime) != sizeof(uint32_t)) {
        break;
      }
      attr->atime = ntohl(attr->atime);
      if (buffer_get_u32(buf, &attr->mtime) != sizeof(uint32_t)) {
        break;
      }
      attr->mtime = ntohl(attr->mtime);
    }

    if (flags & SSH_FILEXFER_ATTR_EXTENDED) {
      if (buffer_get_u32(buf, &attr->extended_count) != sizeof(uint32_t)) {
        break;
      }

      attr->extended_count = ntohl(attr->extended_count);
      while (attr->extended_count &&
          (attr->extended_type = buffer_get_ssh_string(buf))
          && (attr->extended_data = buffer_get_ssh_string(buf))) {
        attr->extended_count--;
      }

      if (attr->extended_count) {
        break;
      }
    }
    ok = 1;
  } while (0);

  if (!ok) {
    /* break issued somewhere */
    ssh_string_free(name);
    ssh_string_free(longname);
    ssh_string_free(attr->extended_type);
    ssh_string_free(attr->extended_data);
    SAFE_FREE(attr->name);
    SAFE_FREE(attr->longname);
    SAFE_FREE(attr->owner);
    SAFE_FREE(attr->group);
    SAFE_FREE(attr);

    ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure");

    return NULL;
  }

  /* everything went smoothly */
  return attr;
}


int sftp_get_error(sftp_session sftp) {
  if (sftp == NULL) {
    return -1;
  }

  return sftp->errnum;
}


sftp_session sftp_new(ssh_session session){
  sftp_session sftp;

  if (session == NULL) {
    return NULL;
  }
  enter_function();

  sftp = malloc(sizeof(struct sftp_session_struct));
  if (sftp == NULL) {
    ssh_set_error_oom(session);
    leave_function();
    return NULL;
  }
  ZERO_STRUCTP(sftp);

  sftp->ext = sftp_ext_new();
  if (sftp->ext == NULL) {
    ssh_set_error_oom(session);
    SAFE_FREE(sftp);
    leave_function();
    return NULL;
  }

  sftp->session = session;
  sftp->channel = ssh_channel_new(session);
  if (sftp->channel == NULL) {
    SAFE_FREE(sftp);
    leave_function();
    return NULL;
  }

  if (ssh_channel_open_session(sftp->channel)) {
    ssh_channel_free(sftp->channel);
    SAFE_FREE(sftp);
    leave_function();
    return NULL;
  }

  if (ssh_channel_request_sftp(sftp->channel)) {
    sftp_free(sftp);
    leave_function();
    return NULL;
  }

  leave_function();
  return sftp;
}


int sftp_chown(sftp_session sftp, const char *file, uid_t owner, gid_t group) {
	struct sftp_attributes_struct attr;
  ZERO_STRUCT(attr);

  attr.uid = owner;
  attr.gid = group;

  attr.flags = SSH_FILEXFER_ATTR_UIDGID;

  return sftp_setstat(sftp, file, &attr);
}