1 /*
2  *  Copyright (c) 1993 Regents of the University of Michigan.
3  *  All rights reserved.
4  *
5  *  kbind.c
6  */
7 
8 #ifndef lint
9 static char copyright[] = "@(#) Copyright (c) 1993 Regents of the University of Michigan.\nAll rights reserved.\n";
10 #endif
11 
12 #ifdef KERBEROS
13 
14 #include <stdio.h>
15 #include <string.h>
16 
17 #ifdef MACOS
18 #include <stdlib.h>
19 #include "macos.h"
20 #else /* MACOS */
21 #ifdef DOS
22 #include "msdos.h"
23 #endif /* DOS */
24 #include <krb.h>
25 #include <stdlib.h>
26 #if !defined(DOS) && !defined( _WIN32 )
27 #include <sys/types.h>
28 #endif /* !DOS && !_WIN32 */
29 #include <sys/time.h>
30 #include <sys/socket.h>
31 #endif /* MACOS */
32 
33 #include "lber.h"
34 #include "ldap.h"
35 #include "ldap-int.h"
36 
37 
38 
39 /*
40  * ldap_kerberos_bind1 - initiate a bind to the ldap server using
41  * kerberos authentication.  The dn is supplied.  It is assumed the user
42  * already has a valid ticket granting ticket.  The msgid of the
43  * request is returned on success (suitable for passing to ldap_result()),
44  * -1 is returned if there's trouble.
45  *
46  * Example:
47  *	ldap_kerberos_bind1( ld, "cn=manager, o=university of michigan, c=us" )
48  */
49 int
ldap_kerberos_bind1(LDAP * ld,char * dn)50 ldap_kerberos_bind1( LDAP *ld, char *dn )
51 {
52 	BerElement	*ber;
53 	char		*cred;
54 	int		rc, credlen;
55 	char		*get_kerberosv4_credentials();
56 #ifdef STR_TRANSLATION
57 	int		str_translation_on;
58 #endif /* STR_TRANSLATION */
59 
60 	/*
61 	 * The bind request looks like this:
62 	 *	BindRequest ::= SEQUENCE {
63 	 *		version		INTEGER,
64 	 *		name		DistinguishedName,
65 	 *		authentication	CHOICE {
66 	 *			krbv42ldap	[1] OCTET STRING
67 	 *			krbv42dsa	[2] OCTET STRING
68 	 *		}
69 	 *	}
70 	 * all wrapped up in an LDAPMessage sequence.
71 	 */
72 
73 	Debug( LDAP_DEBUG_TRACE, "ldap_kerberos_bind1\n", 0, 0, 0 );
74 
75 	if ( dn == NULL )
76 		dn = "";
77 
78 	if ( (cred = get_kerberosv4_credentials( ld, dn, "ldapserver",
79 	    &credlen )) == NULL ) {
80 		return( -1 );	/* ld_errno should already be set */
81 	}
82 
83 	/* create a message to send */
84 	if ( (ber = alloc_ber_with_options( ld )) == NULLBER ) {
85 		free( cred );
86 		return( -1 );
87 	}
88 
89 #ifdef STR_TRANSLATION
90 	if (( str_translation_on = (( ber->ber_options &
91 	    LBER_TRANSLATE_STRINGS ) != 0 ))) {	/* turn translation off */
92 		ber->ber_options &= ~LBER_TRANSLATE_STRINGS;
93 	}
94 #endif /* STR_TRANSLATION */
95 
96 	/* fill it in */
97 	rc = ber_printf( ber, "{it{isto}}", ++ld->ld_msgid, LDAP_REQ_BIND,
98 	    ld->ld_version, dn, LDAP_AUTH_KRBV41, cred, credlen );
99 
100 #ifdef STR_TRANSLATION
101 	if ( str_translation_on ) {	/* restore translation */
102 		ber->ber_options |= LBER_TRANSLATE_STRINGS;
103 	}
104 #endif /* STR_TRANSLATION */
105 
106 	if ( rc == -1 ) {
107 		free( cred );
108 		ber_free( ber, 1 );
109 		ld->ld_errno = LDAP_ENCODING_ERROR;
110 		return( -1 );
111 	}
112 
113 	free( cred );
114 
115 #ifndef NO_CACHE
116 	if ( ld->ld_cache != NULL ) {
117 		ldap_flush_cache( ld );
118 	}
119 #endif /* !NO_CACHE */
120 
121 	/* send the message */
122 	return ( send_initial_request( ld, LDAP_REQ_BIND, dn, ber ));
123 }
124 
125 int
ldap_kerberos_bind1_s(LDAP * ld,char * dn)126 ldap_kerberos_bind1_s( LDAP *ld, char *dn )
127 {
128 	int		msgid;
129 	LDAPMessage	*res;
130 
131 	Debug( LDAP_DEBUG_TRACE, "ldap_kerberos_bind1_s\n", 0, 0, 0 );
132 
133 	/* initiate the bind */
134 	if ( (msgid = ldap_kerberos_bind1( ld, dn )) == -1 )
135 		return( ld->ld_errno );
136 
137 	/* wait for a result */
138 	if ( ldap_result( ld, ld->ld_msgid, 1, (struct timeval *) 0, &res )
139 	    == -1 ) {
140 		return( ld->ld_errno );	/* ldap_result sets ld_errno */
141 	}
142 
143 	return( ldap_result2error( ld, res, 1 ) );
144 }
145 
146 /*
147  * ldap_kerberos_bind2 - initiate a bind to the X.500 server using
148  * kerberos authentication.  The dn is supplied.  It is assumed the user
149  * already has a valid ticket granting ticket.  The msgid of the
150  * request is returned on success (suitable for passing to ldap_result()),
151  * -1 is returned if there's trouble.
152  *
153  * Example:
154  *	ldap_kerberos_bind2( ld, "cn=manager, o=university of michigan, c=us" )
155  */
156 int
ldap_kerberos_bind2(LDAP * ld,char * dn)157 ldap_kerberos_bind2( LDAP *ld, char *dn )
158 {
159 	BerElement	*ber;
160 	char		*cred;
161 	int		rc, credlen;
162 	char		*get_kerberosv4_credentials();
163 #ifdef STR_TRANSLATION
164 	int		str_translation_on;
165 #endif /* STR_TRANSLATION */
166 
167 	Debug( LDAP_DEBUG_TRACE, "ldap_kerberos_bind2\n", 0, 0, 0 );
168 
169 	if ( dn == NULL )
170 		dn = "";
171 
172 	if ( (cred = get_kerberosv4_credentials( ld, dn, "x500dsa", &credlen ))
173 	    == NULL ) {
174 		return( -1 );	/* ld_errno should already be set */
175 	}
176 
177 	/* create a message to send */
178 	if ( (ber = alloc_ber_with_options( ld )) == NULLBER ) {
179 		free( cred );
180 		return( -1 );
181 	}
182 
183 #ifdef STR_TRANSLATION
184 	if (( str_translation_on = (( ber->ber_options &
185 	    LBER_TRANSLATE_STRINGS ) != 0 ))) {	/* turn translation off */
186 		ber->ber_options &= ~LBER_TRANSLATE_STRINGS;
187 	}
188 #endif /* STR_TRANSLATION */
189 
190 	/* fill it in */
191 	rc = ber_printf( ber, "{it{isto}}", ++ld->ld_msgid, LDAP_REQ_BIND,
192 	    ld->ld_version, dn, LDAP_AUTH_KRBV42, cred, credlen );
193 
194 
195 #ifdef STR_TRANSLATION
196 	if ( str_translation_on ) {	/* restore translation */
197 		ber->ber_options |= LBER_TRANSLATE_STRINGS;
198 	}
199 #endif /* STR_TRANSLATION */
200 
201 	free( cred );
202 
203 	if ( rc == -1 ) {
204 		ber_free( ber, 1 );
205 		ld->ld_errno = LDAP_ENCODING_ERROR;
206 		return( -1 );
207 	}
208 
209 	/* send the message */
210 	return ( send_initial_request( ld, LDAP_REQ_BIND, dn, ber ));
211 }
212 
213 /* synchronous bind to DSA using kerberos */
214 int
ldap_kerberos_bind2_s(LDAP * ld,char * dn)215 ldap_kerberos_bind2_s( LDAP *ld, char *dn )
216 {
217 	int		msgid;
218 	LDAPMessage	*res;
219 
220 	Debug( LDAP_DEBUG_TRACE, "ldap_kerberos_bind2_s\n", 0, 0, 0 );
221 
222 	/* initiate the bind */
223 	if ( (msgid = ldap_kerberos_bind2( ld, dn )) == -1 )
224 		return( ld->ld_errno );
225 
226 	/* wait for a result */
227 	if ( ldap_result( ld, ld->ld_msgid, 1, (struct timeval *) 0, &res )
228 	    == -1 ) {
229 		return( ld->ld_errno );	/* ldap_result sets ld_errno */
230 	}
231 
232 	return( ldap_result2error( ld, res, 1 ) );
233 }
234 
235 /* synchronous bind to ldap and DSA using kerberos */
236 int
ldap_kerberos_bind_s(LDAP * ld,char * dn)237 ldap_kerberos_bind_s( LDAP *ld, char *dn )
238 {
239 	int	err;
240 
241 	Debug( LDAP_DEBUG_TRACE, "ldap_kerberos_bind_s\n", 0, 0, 0 );
242 
243 	if ( (err = ldap_kerberos_bind1_s( ld, dn )) != LDAP_SUCCESS )
244 		return( err );
245 
246 	return( ldap_kerberos_bind2_s( ld, dn ) );
247 }
248 
249 
250 #ifndef AUTHMAN
251 #ifdef WINDOWS
252 #define FreeWindowsLibrary() FreeLibrary(instKrbv4DLL);instKrbv4DLL = (HINSTANCE)NULL;
253 #else /* WINDOWS */
254 #define FreeWindowsLibrary() /* do nothing */
255 #endif /* WINDOWS */
256 /*
257  * get_kerberosv4_credentials - obtain kerberos v4 credentials for ldap.
258  * The dn of the entry to which to bind is supplied.  It's assumed the
259  * user already has a tgt.
260  */
261 
262 char *
get_kerberosv4_credentials(LDAP * ld,char * who,char * service,int * len)263 get_kerberosv4_credentials( LDAP *ld, char *who, char *service, int *len )
264 {
265 	KTEXT_ST	ktxt;
266 	int		err;
267 	char		realm[REALM_SZ], *cred, *krbinstance;
268 #ifdef WINDOWS
269 	typedef int (_cdecl* pfn_krb_get_tf_realm) (char FAR *,char FAR *);
270 	typedef int (PASCAL* pfn_krb_mk_req) (KTEXT,LPSTR,LPSTR,LPSTR,long);
271 	typedef char * (_cdecl* pfn_tkt_string) ();
272 
273     	static HINSTANCE instKrbv4DLL = (HINSTANCE)NULL;
274     	pfn_krb_get_tf_realm fptr_krb_get_tf_realm = NULL;
275     	pfn_krb_mk_req fptr_krb_mk_req = NULL;
276     	pfn_tkt_string fptr_tkt_string = NULL;
277     	char* p_tkt_string = NULL;
278     	KTEXT pKt = &ktxt;
279 #endif
280 
281 	Debug( LDAP_DEBUG_TRACE, "get_kerberosv4_credentials\n", 0, 0, 0 );
282 
283 #ifdef WINDOWS
284 	/*
285 	 * The goal is to gracefully survive the absence of krbv4win.dll
286 	 * and thus wshelper.dll.  User's won't be able to use kerberos,
287 	 * but they shouldn't see a message box everytime libldap.dll loads.
288 	 */
289     	if ( !instKrbv4DLL ) {
290         	unsigned int prevMode = SetErrorMode( SEM_NOOPENFILEERRORBOX ); // don't whine at user if you can't find it
291         	instKrbv4DLL = LoadLibrary("Krbv4win.DLL");
292         	SetErrorMode( prevMode );
293 
294         	if ( instKrbv4DLL < HINSTANCE_ERROR ) { // can't find authlib
295             		ld->ld_errno = LDAP_AUTH_UNKNOWN;
296             		return( NULL );
297         	}
298 
299         	fptr_krb_get_tf_realm = (pfn_krb_get_tf_realm)GetProcAddress( instKrbv4DLL, "_krb_get_tf_realm" );
300         	fptr_krb_mk_req = (pfn_krb_mk_req)GetProcAddress( instKrbv4DLL, "krb_mk_req" );
301         	fptr_tkt_string = (pfn_tkt_string)GetProcAddress( instKrbv4DLL, "_tkt_string" );
302 
303         	// verify that we found all the routines we need
304         	if (!(fptr_krb_mk_req && fptr_krb_get_tf_realm && fptr_tkt_string)) {
305         		FreeWindowsLibrary();
306             		ld->ld_errno = LDAP_AUTH_UNKNOWN;
307             		return( NULL );
308         	}
309 
310     	}
311 	p_tkt_string = (fptr_tkt_string)( );
312 	if ( (err = (fptr_krb_get_tf_realm)( p_tkt_string, realm )) != KSUCCESS ) {
313 #else /* WINDOWS */
314 	if ( (err = krb_get_tf_realm( tkt_string(), realm )) != KSUCCESS ) {
315 #endif /* WINDOWS */
316 #ifndef NO_USERINTERFACE
317 		fprintf( stderr, "krb_get_tf_realm failed (%s)\n",
318 		    krb_err_txt[err] );
319 #endif /* NO_USERINTERFACE */
320 		ld->ld_errno = LDAP_INVALID_CREDENTIALS;
321        		FreeWindowsLibrary();
322 		return( NULL );
323 	}
324 
325 #ifdef LDAP_REFERRALS
326 	krbinstance = ld->ld_defconn->lconn_krbinstance;
327 #else /* LDAP_REFERRALS */
328 	krbinstance = ld->ld_host;
329 #endif /* LDAP_REFERRALS */
330 
331 #ifdef WINDOWS
332    if ( !krbinstance ) {	// if we don't know name of service host, no chance for service tickets
333        	FreeWindowsLibrary();
334         ld->ld_errno = LDAP_LOCAL_ERROR;
335         WSASetLastError(WSANO_ADDRESS);
336     	return( NULL );
337     }
338 #endif /* WINDOWS */
339 
340 #ifdef WINDOWS
341 	if ( (err = (fptr_krb_mk_req)( pKt, service, krbinstance, realm, 0 ))
342 #else /* WINDOWS */
343 	if ( (err = krb_mk_req( &ktxt, service, krbinstance, realm, 0 ))
344 #endif /* WINDOWS */
345 	    != KSUCCESS ) {
346 #ifndef NO_USERINTERFACE
347 		fprintf( stderr, "krb_mk_req failed (%s)\n", krb_err_txt[err] );
348 #endif /* NO_USERINTERFACE */
349 		ld->ld_errno = LDAP_INVALID_CREDENTIALS;
350        		FreeWindowsLibrary();
351 		return( NULL );
352 	}
353 
354 	if ( ( cred = malloc( ktxt.length )) == NULL ) {
355 		ld->ld_errno = LDAP_NO_MEMORY;
356        		FreeWindowsLibrary();
357 		return( NULL );
358 	}
359 
360 	*len = ktxt.length;
361 	memcpy( cred, ktxt.dat, ktxt.length );
362 
363    	FreeWindowsLibrary();
364 	return( cred );
365 }
366 
367 #endif /* !AUTHMAN */
368 #endif /* KERBEROS */
369