Prevent potential side effects and use local variables here
[pdns-ldap-backend/landonf.git] / src / ldapauthenticator.cc
blobce63f10bb9ae7c30836bb4d50f8eb1b251957950
1 /*
2 * PowerDNS LDAP Backend
3 * Copyright (C) 2011 Grégory Oestreicher <greg@kamago.net>
4 * Copyright (C) 2003-2007 Norbert Sendetzky <norbert@linuxnetworks.de>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2
8 * as published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include <krb5.h>
21 #include <pdns/logger.hh>
22 #include "ldapauthenticator_p.hh"
23 #include "ldaputils.hh"
25 /*****************************
27 * LdapSimpleAuthenticator
29 ****************************/
31 LdapSimpleAuthenticator::LdapSimpleAuthenticator( const std::string& dn, const std::string& pw, int tmout )
32 : binddn( dn ), bindpw( pw ), timeout( tmout )
36 bool LdapSimpleAuthenticator::authenticate( LDAP *conn )
38 int msgid;
40 #ifdef HAVE_LDAP_SASL_BIND
41 int rc;
42 struct berval passwd;
44 passwd.bv_val = (char *)bindpw.c_str();
45 passwd.bv_len = strlen( passwd.bv_val );
47 if( ( rc = ldap_sasl_bind( conn, binddn.c_str(), LDAP_SASL_SIMPLE, &passwd, NULL, NULL, &msgid ) ) != LDAP_SUCCESS )
49 fillLastError( conn, rc );
50 return false;
52 #else
53 if( ( msgid = ldap_bind( conn, binddn.c_str(), bindpw.c_str(), LDAP_AUTH_SIMPLE ) ) == -1 )
55 fillLastError( conn, msgid );
56 return false;
58 #endif
60 ldapWaitResult( conn, msgid, timeout, NULL );
61 return true;
64 std::string LdapSimpleAuthenticator::getError() const
66 return lastError;
69 void LdapSimpleAuthenticator::fillLastError( LDAP* conn, int code )
71 lastError = ldapGetError( conn, code );
74 /*****************************
76 * LdapGssapiAuthenticator
78 ****************************/
80 static int ldapGssapiAuthenticatorSaslInteractCallback( LDAP *conn, unsigned flags, void *defaults, void *in )
82 return LDAP_SUCCESS;
85 LdapGssapiAuthenticator::LdapGssapiAuthenticator( const std::string& kt, const std::string &ccache, int tmout )
86 : keytabFile( kt ), cCacheFile( ccache ), timeout( tmout )
90 bool LdapGssapiAuthenticator::authenticate( LDAP *conn )
92 Logger L( "LDAP GSSAPI" );
93 int code = attemptAuth( conn );
95 if ( code == -1 ) {
96 return false;
98 else if ( code == -2 ) {
99 // Here it may be possible to retry after obtainting a fresh ticket
100 L<<Logger::Debug << "No TGT found, trying to acquire a new one" << std::endl;
101 code = updateTgt();
103 if ( attemptAuth( conn ) != 0 ) {
104 L<<Logger::Error << "Failed to acquire a TGT" << std::endl;
105 return false;
109 return true;
112 std::string LdapGssapiAuthenticator::getError() const
114 return lastError;
117 int LdapGssapiAuthenticator::attemptAuth( LDAP *conn )
119 Logger L( "LDAP GSSAPI" );
120 // Create SASL defaults
121 SaslDefaults defaults;
122 char *ldapOption = 0;
124 ldap_get_option( conn, LDAP_OPT_X_SASL_MECH, ldapOption );
125 if ( !ldapOption )
126 defaults.mech = std::string( "GSSAPI" );
127 else
128 defaults.mech = std::string( ldapOption );
129 ldap_memfree( ldapOption );
131 ldap_get_option( conn, LDAP_OPT_X_SASL_REALM, ldapOption );
132 if ( ldapOption )
133 defaults.realm = std::string( ldapOption );
134 ldap_memfree( ldapOption );
136 ldap_get_option( conn, LDAP_OPT_X_SASL_AUTHCID, ldapOption );
137 if ( ldapOption )
138 defaults.authcid = std::string( ldapOption );
139 ldap_memfree( ldapOption );
141 ldap_get_option( conn, LDAP_OPT_X_SASL_AUTHZID, ldapOption );
142 if ( ldapOption )
143 defaults.authzid = std::string( ldapOption );
144 ldap_memfree( ldapOption );
146 // And now try to bind
147 int rc = ldap_sasl_interactive_bind_s( conn, "", defaults.mech.c_str(),
148 NULL, NULL, LDAP_SASL_QUIET,
149 ldapGssapiAuthenticatorSaslInteractCallback, &defaults );
150 L<<Logger::Debug << "ldap_sasl_interactive_bind_s returned " << rc << std::endl;
152 if ( rc == LDAP_LOCAL_ERROR ) {
153 // This may mean that the ticket has expired, so let the caller know
154 lastError = ldapGetError( conn, rc );
155 return -2;
157 else if ( rc != LDAP_SUCCESS ) {
158 lastError = ldapGetError( conn, rc );
159 return -1;
162 return rc;
165 int LdapGssapiAuthenticator::updateTgt()
167 Logger L( "LDAP GSSAPI" );
168 krb5_error_code code;
169 krb5_context context;
170 krb5_creds credentials;
171 krb5_keytab keytab;
172 krb5_principal principal;
173 krb5_ccache ccache;
174 krb5_get_init_creds_opt *options;
176 if ( ( code = krb5_init_context( &context ) ) != 0 ) {
177 L<<Logger::Error << "Failed to init krb5 context" << std::endl;
178 return code;
181 if ( !keytabFile.empty() ) {
182 std::string keytabStr( "FILE:" + keytabFile );
183 code = krb5_kt_resolve( context, keytabStr.c_str(), &keytab );
185 else {
186 code = krb5_kt_default( context, &keytab );
189 if ( code != 0 ) {
190 L<<Logger::Error << "krb5 error: " << std::string( krb5_get_error_message( context, code ) ) << std::endl;
191 return code;
194 // Extract the principal name from the keytab
195 krb5_kt_cursor cursor;
196 if ( ( code = krb5_kt_start_seq_get( context, keytab, &cursor ) ) != 0 ) {
197 L<<Logger::Error << "krb5 error: " << std::string( krb5_get_error_message( context, code ) ) << std::endl;
198 krb5_kt_close( context, keytab );
199 return code;
202 krb5_keytab_entry entry;
203 if ( ( code = krb5_kt_next_entry( context, keytab, &entry, &cursor ) ) == 0 ) {
204 code = krb5_copy_principal( context, entry.principal, &principal );
205 krb5_kt_free_entry( context, &entry );
208 krb5_kt_end_seq_get( context, keytab, &cursor );
209 if ( code != 0 ) {
210 L<<Logger::Error << "krb5 error: " << std::string( krb5_get_error_message( context, code ) ) << std::endl;
211 krb5_kt_close( context, keytab );
212 krb5_free_principal( context, principal );
213 return code;
216 // Locate the credentials cache file
217 if ( !cCacheFile.empty() ) {
218 std::string cCacheStr( "FILE:" + cCacheFile );
219 code = krb5_cc_resolve( context, cCacheStr.c_str(), &ccache );
221 else {
222 code = krb5_cc_default( context, &ccache );
225 if ( code != 0 ) {
226 L<<Logger::Error << "krb5 error: " << std::string( krb5_get_error_message( context, code ) ) << std::endl;
227 krb5_kt_close( context, keytab );
228 krb5_free_principal( context, principal );
229 return code;
232 // Initialize the credentials cache file
233 if ( ( code = krb5_cc_initialize( context, ccache, principal ) ) != 0 ) {
234 L<<Logger::Error << "krb5 error: " << std::string( krb5_get_error_message( context, code ) ) << std::endl;
235 krb5_kt_close( context, keytab );
236 krb5_free_principal( context, principal );
237 return code;
240 if ( ( code = krb5_get_init_creds_opt_alloc( context, &options ) ) != 0 ) {
241 L<<Logger::Error << "krb5 error: " << std::string( krb5_get_error_message( context, code ) ) << std::endl;
242 krb5_kt_close( context, keytab );
243 krb5_free_principal( context, principal );
244 return code;
246 krb5_get_init_creds_opt_set_default_flags( context, "pdns", NULL, options );
248 // And finally get the TGT!
249 code = krb5_get_init_creds_keytab( context, &credentials, principal, keytab, 0, NULL, options );
250 krb5_get_init_creds_opt_free( context, options );
251 krb5_kt_close( context, keytab );
252 krb5_free_principal( context, principal );
254 if ( code == 0 ) {
255 L<<Logger::Error << "krb5 error: " << std::string( krb5_get_error_message( context, code ) ) << std::endl;
256 code = krb5_cc_store_cred( context, ccache, &credentials );
257 krb5_free_cred_contents( context, &credentials );
258 krb5_cc_close( context, ccache );
261 krb5_free_context( context );
262 return code;