1// $OpenLDAP$
2/*
3 * Copyright 2010-2021 The OpenLDAP Foundation, All Rights Reserved.
4 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5 */
6
7#include <fstream>
8#include <sstream>
9#include <sys/types.h>
10#include <sys/stat.h>
11#include <errno.h>
12#include <unistd.h>
13#include <cstring>
14#include "TlsOptions.h"
15#include "LDAPException.h"
16
17enum opttype {
18    INT=0,
19    STRING,
20    OTHER
21};
22
23typedef struct tls_optmap {
24    int optval;
25    opttype type;
26} tls_optmap_t;
27
28static tls_optmap_t optmap[] = {
29    { LDAP_OPT_X_TLS_CACERTFILE, STRING },
30    { LDAP_OPT_X_TLS_CACERTDIR, STRING },
31    { LDAP_OPT_X_TLS_CERTFILE, STRING },
32    { LDAP_OPT_X_TLS_KEYFILE, STRING },
33    { LDAP_OPT_X_TLS_REQUIRE_CERT, INT },
34    { LDAP_OPT_X_TLS_PROTOCOL_MIN, INT },
35    { LDAP_OPT_X_TLS_CIPHER_SUITE, STRING },
36    { LDAP_OPT_X_TLS_RANDOM_FILE, STRING },
37    { LDAP_OPT_X_TLS_CRLCHECK, INT },
38    { LDAP_OPT_X_TLS_DHFILE, STRING },
39    { LDAP_OPT_X_TLS_NEWCTX, INT }
40};
41#if 0 /* not implemented currently */
42        static const int TLS_CRLFILE /* GNUtls only */
43        static const int TLS_SSL_CTX  /* OpenSSL SSL* */
44        static const int TLS_CONNECT_CB
45        static const int TLS_CONNECT_ARG
46#endif
47
48static void checkOpt( TlsOptions::tls_option opt, opttype type ) {
49    if ( opt < TlsOptions::CACERTFILE || opt >= TlsOptions::LASTOPT ){
50        throw( LDAPException( LDAP_PARAM_ERROR, "unknown Option" ) );
51    }
52
53    if ( optmap[opt].type != type ){
54        throw( LDAPException( LDAP_PARAM_ERROR, "not a string option" ) );
55    }
56}
57
58TlsOptions::TlsOptions() : m_ld(NULL) {}
59
60TlsOptions::TlsOptions( LDAP* ld ): m_ld(ld) { }
61
62void TlsOptions::setOption( tls_option opt, const std::string& value ) const {
63    checkOpt(opt, STRING);
64    switch(opt) {
65        case TlsOptions::CACERTFILE :
66        case TlsOptions::CERTFILE :
67        case TlsOptions::KEYFILE :
68        {
69            // check if the supplied file is actually readable
70            std::ifstream ifile(value.c_str());
71            if ( !ifile ) {
72                throw( LDAPException( LDAP_LOCAL_ERROR, "Unable to open the supplied file for reading" ) );
73            }
74        }
75        break;
76        case TlsOptions::CACERTDIR :
77        {
78            struct stat st;
79            std::ostringstream msg;
80            bool fail=false;
81            int err = stat(value.c_str(),&st);
82            if ( err ) {
83                msg << strerror(errno);
84                fail = true;
85            } else {
86                if ( !S_ISDIR(st.st_mode) ){
87                    msg << "The supplied path is not a directory.";
88                    fail = true;
89                }
90            }
91            if ( fail ) {
92                std::ostringstream errstr;
93                errstr << "Error while setting Certificate Directory (" << value << "): " << msg.str();
94                throw( LDAPException( LDAP_LOCAL_ERROR, errstr.str() ) );
95            }
96        }
97        break;
98    }
99    this->setOption( opt, value.empty() ? NULL : (void*) value.c_str() );
100}
101
102void TlsOptions::setOption( tls_option opt, int value ) const {
103    checkOpt(opt, INT);
104    this->setOption( opt, (void*) &value);
105}
106
107void TlsOptions::setOption( tls_option opt, void *value ) const {
108    int ret = ldap_set_option( m_ld, optmap[opt].optval, value);
109    if ( ret != LDAP_OPT_SUCCESS )
110    {
111        if ( ret != LDAP_OPT_ERROR ){
112            throw( LDAPException( ret ));
113        } else {
114            throw( LDAPException( LDAP_PARAM_ERROR, "error while setting TLS option" ) );
115        }
116    }
117    this->newCtx();
118}
119
120void TlsOptions::getOption( tls_option opt, void* value ) const {
121    int ret = ldap_get_option( m_ld, optmap[opt].optval, value);
122    if ( ret != LDAP_OPT_SUCCESS )
123    {
124        if ( ret != LDAP_OPT_ERROR ){
125            throw( LDAPException( ret ));
126        } else {
127            throw( LDAPException( LDAP_PARAM_ERROR, "error while reading TLS option" ) );
128        }
129    }
130}
131
132int TlsOptions::getIntOption( tls_option opt ) const {
133    int value;
134    checkOpt(opt, INT);
135    ldap_get_option( m_ld, optmap[opt].optval, (void*) &value);
136    return value;
137}
138
139std::string TlsOptions::getStringOption( tls_option opt ) const {
140    char *value;
141    checkOpt(opt, STRING);
142    ldap_get_option( m_ld, optmap[opt].optval, (void*) &value);
143    std::string strval;
144    if (value)
145    {
146        strval=std::string(value);
147        ldap_memfree(value);
148    }
149    return strval;
150}
151
152void TlsOptions::newCtx() const {
153    int val = 0;
154    int ret = ldap_set_option( m_ld, LDAP_OPT_X_TLS_NEWCTX, &val);
155    if ( ret != LDAP_OPT_SUCCESS )
156    {
157        if ( ret != LDAP_OPT_ERROR ){
158            throw( LDAPException( ret ));
159        } else {
160            throw( LDAPException( LDAP_LOCAL_ERROR, "error while renewing TLS context" ) );
161        }
162    }
163}
164