/* vim:set sw=4 ts=8 fileencoding=cp1251::Кодировка:WINDOWS-1251[АБЁЪЯабёъя] */
/*
 * Copyright(C) 2013 Проект ИОК
 *
 * Этот файл содержит информацию, являющуюся
 * собственностью компании Крипто Про.
 *
 * Любая часть этого файла не может быть скопирована,
 * исправлена, переведена на другие языки,
 * локализована или модифицирована любым способом,
 * откомпилирована, передана по сети с или на
 * любую компьютерную систему без предварительного
 * заключения соглашения с компанией Крипто Про.
 *
 * Программный код, содержащийся в этом файле, предназначен
 * исключительно для целей обучения и не может быть использован
 * для защиты информации.
 *
 * Компания Крипто-Про не несет никакой
 * ответственности за функционирование этого кода.
 */

/*!
 * \file $RCSfile$
 * \version $Revision: 59519 $
 * \date $Date: 2010-01-13 15:36:15 +0300 (Wed, 13 Jan 2010) $
 * \author $Author: dedal $
 *
 * \brief Воплощение в жизнь ioctl для базовых функций загружаемого модуля 
 * "КриптоПро KTLS" под Unix
 *

 */
#include"WCKernelPart.h"
#include<sys/ioctl.h>
#include<limits.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
const char * dev_name="/dev/drtktls";
struct _DRTKTLS_DRV_
{
    int fd;
    char name[PATH_MAX];
};
struct _DRTKTLS_CTX_
{
    PDRTKTLS_DRV pDrv;
};
SECURITY_STATUS CAPI_EXTC kinit_gost(PDRTKTLS_DRV pDrv, unsigned * pSize)
{
    if (pDrv == 0)
    {
	*pSize=sizeof(DRTKTLS_DRV);
	return STATUS_SUCCESS;
    }
    if (*pSize <sizeof(DRTKTLS_DRV))
    {
	*pSize=sizeof(DRTKTLS_DRV);
	return STATUS_NO_MEMORY;
    }
    strcpy(pDrv->name,dev_name);
    *pSize=sizeof(DRTKTLS_DRV);
    pDrv->fd=open(pDrv->name,O_RDWR);
    if (pDrv->fd < 0)
    {
	return STATUS_INTERNAL_ERROR;
    }
    return STATUS_SUCCESS;
}
SECURITY_STATUS CAPI_EXTC kdone_gost(PDRTKTLS_DRV pDrv)
{
    close(pDrv->fd);
    return STATUS_SUCCESS;
}
SECURITY_STATUS CAPI_EXTC kinit_ctx(PDRTKTLS_DRV pDrv,PDRTKTLS_CTX pDCtx, unsigned * pSize)
{
    if (pDCtx == 0)
    {
	*pSize=sizeof(DRTKTLS_CTX);
	return STATUS_SUCCESS;
    }
    if (*pSize <sizeof(DRTKTLS_CTX))
    {
	*pSize=sizeof(DRTKTLS_CTX);
	return STATUS_NO_MEMORY;
    }
    pDCtx->pDrv=pDrv;
    return STATUS_SUCCESS;
}
SECURITY_STATUS CAPI_EXTC kdone_ctx(PDRTKTLS_CTX pCtx)
{
    return STATUS_SUCCESS;
}

SECURITY_STATUS CAPI_EXTC kset_rng(PDRTKTLS_DRV pDrv, unsigned char * pbData,unsigned cbData)
{
    DRTKTLS_EXCHANGE Ex;
    Ex.pvBuffer=pbData;
    Ex.cbBuffer=cbData;
    if (ioctl(pDrv->fd,DRTKTLS_RNG,&Ex) == -1)
	return STATUS_INTERNAL_ERROR;
    return STATUS_SUCCESS;	
}

SECURITY_STATUS CAPI_EXTC kcreate_SA(PDRTKTLS_DRV pDrv)
{
     if (ioctl(pDrv->fd,DRTKTLS_CREATE_SA) == -1)
	return STATUS_INTERNAL_ERROR;
    return STATUS_SUCCESS;	
}

SECURITY_STATUS CAPI_EXTC kexport_SA(PDRTKTLS_DRV pDrv,unsigned char * pbBuffer,unsigned * pcbBuffer)
{
    DRTKTLS_EXCHANGE Ex;
    Ex.pvBuffer=pbBuffer;
    Ex.cbBuffer=*pcbBuffer;
    if (ioctl(pDrv->fd,DRTKTLS_EXPORT_SA,&Ex) == -1)
    {
	switch(errno)
	{
	    case ENOMEM:
		*pcbBuffer=Ex.cbBuffer;
		return STATUS_NO_MEMORY;
	    default:	
		return STATUS_INTERNAL_ERROR;
	}
    }
    *pcbBuffer=Ex.cbBuffer;
    return STATUS_SUCCESS;	
}
SECURITY_STATUS CAPI_EXTC kimport_SA(PDRTKTLS_CTX pDCtx, unsigned char * pbSA,unsigned cbSA)
{
    DRTKTLS_EXCHANGE Ex;
    Ex.pvBuffer=pbSA;
    Ex.cbBuffer=cbSA;
    if (ioctl(pDCtx->pDrv->fd,DRTKTLS_IMPORT_SA,&Ex) == -1)
	return STATUS_INTERNAL_ERROR;
    return STATUS_SUCCESS;	
}

SECURITY_STATUS CAPI_EXTC kexport_keys(PDRTKTLS_CTX pDCtx, void * pvExpDCtx, unsigned *pcbExpDCtx)
{
    DRTKTLS_EXCHANGE Ex;
    Ex.pvBuffer=pvExpDCtx;
    Ex.cbBuffer=*pcbExpDCtx;
    if (ioctl(pDCtx->pDrv->fd,DRTKTLS_EXPORT_CONTEXT,&Ex) == -1)
    {
	switch (errno)
	{
	    case ENOMEM:
		*pcbExpDCtx=Ex.cbBuffer;
		return STATUS_NO_MEMORY;
	    default:
		return STATUS_INTERNAL_ERROR;
	}
    }
    *pcbExpDCtx=Ex.cbBuffer;
    return STATUS_SUCCESS;	
}

SECURITY_STATUS CAPI_EXTC kimport_keys(PDRTKTLS_CTX pDCtx,void * pvExpDCtx, unsigned cbExpDCtx)
{
    DRTKTLS_EXCHANGE Ex;
    Ex.pvBuffer=pvExpDCtx;
    Ex.cbBuffer=cbExpDCtx;
    if (ioctl(pDCtx->pDrv->fd,DRTKTLS_IMPORT_CONTEXT,&Ex) == -1)
	return STATUS_INTERNAL_ERROR;
    return STATUS_SUCCESS;	
}

SECURITY_STATUS CAPI_EXTC kreadall(PDRTKTLS_CTX pDCtx, int Socket, void * pvBuffer, unsigned * pcbBuffer)
{
    DRTKTLS_PROCESS Pr; 
    SECURITY_STATUS scRet=0;
    Pr.Socket=Socket;
    Pr.Extra.pvBuffer=pvBuffer;
    Pr.Extra.cbBuffer=*pcbBuffer;
    if (ioctl(pDCtx->pDrv->fd,DRTKTLS_PROCESS_DATA,&Pr) == -1)
    {
	switch (errno)
	{
	    case EAGAIN:
		scRet=SEC_I_RENEGOTIATE;
		*pcbBuffer=Pr.Extra.cbBuffer;
		break;
	    default:    
		scRet=STATUS_INTERNAL_ERROR;
	}
    }
    else
    {
	    *pcbBuffer=Pr.Extra.cbBuffer;
    }
    return scRet;	
}

