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 
17 enum opttype {
18     INT=0,
19     STRING,
20     OTHER
21 };
22 
23 typedef struct tls_optmap {
24     int optval;
25     opttype type;
26 } tls_optmap_t;
27 
28 static 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 
checkOpt(TlsOptions::tls_option opt,opttype type)48 static 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 
TlsOptions()58 TlsOptions::TlsOptions() : m_ld(NULL) {}
59 
TlsOptions(LDAP * ld)60 TlsOptions::TlsOptions( LDAP* ld ): m_ld(ld) { }
61 
setOption(tls_option opt,const std::string & value) const62 void 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 
setOption(tls_option opt,int value) const102 void TlsOptions::setOption( tls_option opt, int value ) const {
103     checkOpt(opt, INT);
104     this->setOption( opt, (void*) &value);
105 }
106 
setOption(tls_option opt,void * value) const107 void 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 
getOption(tls_option opt,void * value) const120 void 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 
getIntOption(tls_option opt) const132 int 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 
getStringOption(tls_option opt) const139 std::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 
newCtx() const152 void 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