// aplKerberos.h
// Переделан из https://vm-svn-pss/svn/pss/trunk/SRC/__QT/_StepData/aplSocketTransportQt/aplKerberos.h
//#pragma once

#ifndef CAPLKERBEROS_H
#define CAPLKERBEROS_H

//VS #include <QString>

//#include <windows.h>	
#include "aplSocketTransport.h"
#include <aplAggr.h>  // Это вместо <windows.h>, т.к. <windows.h> конфликтует с послед. использованием aplAgg

#ifdef __linux__

    #include <QStringList>

    #include <gssapi.h>
    #include <krb5/krb5.h>

#else

    //#pragma comment (lib,"Secur32.lib")

    #define SECURITY_WIN32 //при компиляции приложения пользовательского режима.
    //#define SECURITY_KERNEL при компиляции модуля режима ядра.

    #include <sspi.h>

#endif

class SOCKET_TRANSPORT_API CaplKerberos
{
public:

	CString m_class_info;
    CString m_csError; // Текст сообщение о последней ошибке //VS

    CaplKerberos();
    virtual ~CaplKerberos();

    virtual void Clear(); // Закрывает контексты и освобождает все выделенные буфера

    // Инициализирует параметры подключения.
    // В Windows, по сути, ничего не вызывает. Инициализирует внутр. структуры и всё.
    //     Реальная проверка пользователя происходит при первом вызове ProcessingBuffer (с пустым вх. буфером).
    // В Linux проверка пользователя (получение TGT) происходит здесь.
    //
    // qsTargetName - SPN (Service Principal Name) в формате типа "HTTP/server1.qqq.com@QQQ.COM."
    // qsTargetName проверяется не здесь, а при вызове ProcessingBuffer
    //
    // В Windows qsTargetName нужен только для клиента. На сервере не используется совсем.
    // В Linux qsTargetName  должен совпадать с тем, что указал клиент. (Иначе Kerberos не сможет расшифровать пакет)
    //
    // Если qsUser пустой, то используется текущий пользователь ОС и qsPassword игнорируется
    // В Windows:
    // - Если qsDomain пустой, то используется текущий домен
    // - На сервере указанный пользователь используется для получения информации из AD
    //
    // В Linux:
    // - на сервере qsUser, qsPassword и qsDomain игнорируются
    // - на клиенте qsUser должно быть в формате user@REALM, но, если @REALM не задан,
    // то используется qsDomain, а, если не задан и qsDomain, то REALM берется из qsTargetName
	//
	// В Linux для работы сервера нужен файл keytab. Его можно задать двумя способами: 1) установить переменную
	// окружения KRB5_KTNAME с путем к файлу 2) передать путь к файлу пятым параметром функции Init.
	// В Windows параметр qsKeytab не используется.

	bool Init(CString csTargetName, CString csUser, CString csPassword, CString csDomain, CString csKeytab=_T(""));


    // Функция обработки буфера с данными Kerberos.
    // Сначала клиент вызывает ее в нулевым входным буфером. Получает выходной буфер (если не было ошибки).
    // Отравляет его серверу, и, если надо, ждет новый буфер от сервера. и т.д.
    // Сервер получает буфер от клиента. Обрабатывает, и если надо, подучает буфер для отправки клиенту.  Если надо, то ждет след. буфер.
    //
    // pInBuffer и iInBufferSize - входной буфер и его размер. Для начала соедиения у клиента должен быть равен нулю. На сервере нулю не может быть никогда.
    // pOutBuffer и OutBufferSize - выходной буфер и его размер. (выделяется внутри, удалять не надо - удаляется автоматом в деструкторе)
    // Если выходной буфер не ноль, то его надо отправить другой стороне (даже если аутентификация на клиенте успешно прошла)
    // Результат работы:
    // 0 - аутентификация завершена без ошибок (но, если есть вых. буфер, его надо отправить)
    // 1 - обработка продолжается  (если есть вых. буфер, его надо отправить).
    // -1 - аутентификация завершена c ошибкой (ничего отправлять не надо)
    // В m_sError будет описание ошибки,  а в m_secStatus - её код

    virtual int ProcessingBuffer(unsigned char *pInBuffer, int iInBufferSize, unsigned char *&pOutBuffer, int &OutBufferSize)= 0;

protected:

    bool m_bIsServer; // В CaplKerberosServer устанавливается в true

    unsigned char *m_pOutBuffer;
    int m_iOutBufferSize;

	CString m_csInitUser, m_csInitDomain;  // Запоминаются параметры переданные в Init

    // Запоминается параметр переданный в Init (т.к. используется потом)
    // В Windows нужен только в клиенте.
    // В Linux нужен и в клиенте и в сервере.
    CString m_csTargetName;


#ifdef __linux__

    // Служебные функции

    // Преобразует OID в строку
    QString GetOIDAsString(gss_OID oid);


    // Возвращает список имен типов (OID) для Kerberos
    // Нужна только для исследования ОС
    // { 1 2 840 113554 1 2 2 } - Kerberos Version 5
    // { 1 3 6 1 5 5 2 } - NEGOtiation (samba)
    // { 1 2 840 113554 1 2 1 1 }  user-name
    // { 1 2 840 113554 1 2 1 2 }  machine-uid-name
    // { 1 2 840 113554 1 2 1 3 }  string-uid-name
    // { 1 2 840 113554 1 2 1 4 }  Host-based service name form
    // { 1 2 840 113554 1 2 2 1 }  Kerberos principal name form
    // { 1 2 840 113554 1 2 2 2 }  Principal
    // { 1 3 6 1 5 6 2 }  host based services
    // { 1 3 6 1 5 6 4 }  Name objects of the Mechanism-Independent Exported Name
    // { 1 3 6 1 5 6 6 }  gss-composite-export name system designator
    bool GetAllKerberosNameTypes(QStringList &qslKerbNameTypes);

    gss_cred_id_t m_cred;
    krb5_context m_krb5_ctx;
    gss_ctx_id_t m_gss_ctx;

    gss_OID m_KerbOID;  // (OID) для Kerberos
    gss_OID m_KerbNameOID; // (OID) для Имен Kerberos

#else

    SECURITY_STATUS m_secStatus; // Статус последней операции c SSPI

    ULONG m_cbMaxToken;     // Текущее кол-во токенов в настройке ОС (используется как размер для выделения буферов)

    CredHandle m_hCred;     // Контекст подключения - пользователь, под которым выполняются операции
    ULONG m_reqCtxtAttrs;
    CtxtHandle m_hCtxt;
    ULONG m_ctxtAttr;
    bool m_fHaveCtxtHandle;
#endif
};


class SOCKET_TRANSPORT_API CaplKerberosClient :public CaplKerberos
{
public:

    CaplKerberosClient();

	virtual int ProcessingBuffer(unsigned char *pInBuffer, INT32 iInBufferSize, unsigned char *&pOutBuffer, INT32 &OutBufferSize);
};


// Класс проверки аутентификации на сервере.
// Работает только с одним подключением
// (для каждого подключения пользователя должен быть свой экземпляр)
class SOCKET_TRANSPORT_API CaplKerberosServer  :public CaplKerberos
{

public:

    CaplKerberosServer();

    virtual void Clear(); // Закрывает контексты и освобождает все выделенные буфера

	virtual int ProcessingBuffer(unsigned char *pInBuffer, INT32 iInBufferSize, unsigned char *&pOutBuffer, INT32 &OutBufferSize);

    CString getConnectedUser(){return m_csUser;};
    CString getConnectedClient(){return m_csClientName;};
    CString getConnectedServer(){return m_csServerName;};

protected:

    CString m_csUser;       // Имя подключенного пользователя ("AD97\da1").  Заполняется только в Windows при успешном подключении
    CString m_csClientName; // Имя подключенного клиента  ("da1@AD97.TEST").  Заполняется при успешном подключении

    // Полный SPN, для которого выполнена аутентификация ("ils/ad97-cli.ad97.test@AD97.TEST").
    // В Windows это параметр переданный клиенту в Init и переданный механизмами kerberos серверу
    // В Linux просто присваивается занчение m_qsTargetName
    CString m_csServerName;
};



#endif // CAPLKERBEROS_H
