]> git.sur5r.net Git - openldap/blob - clients/ud/auth.c
Add OpenLDAP RCSid to *.[ch] in clients, libraries, and servers.
[openldap] / clients / ud / auth.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright (c) 1991, 1992 Regents of the University of Michigan.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted
7  * provided that this notice is preserved and that due credit is given
8  * to the University of Michigan at Ann Arbor. The name of the University
9  * may not be used to endorse or promote products derived from this
10  * software without specific prior written permission. This software
11  * is provided ``as is'' without express or implied warranty.
12  */
13
14 #include "portable.h"
15
16 #include <stdio.h>
17
18 #include <ac/stdlib.h>
19
20 #include <ac/ctype.h>
21 #include <ac/krb.h>
22 #include <ac/string.h>
23 #include <ac/time.h>
24 #include <ac/unistd.h>
25
26 #ifdef HAVE_PWD_H
27 #include <pwd.h>
28 #endif
29
30 #include <lber.h>
31 #include <ldap.h>
32
33 #include "ldap_defaults.h"
34 #include "ud.h"
35
36 #ifdef HAVE_KERBEROS
37 static char tktpath[20];        /* ticket file path */
38 static int kinit();
39 static int valid_tgt();
40 #endif
41
42 static void set_bound_dn(char *s);
43
44
45 int
46 auth( char *who, int implicit )
47 {
48         int rc;                 /* return code from ldap_bind() */
49         char *passwd = NULL;    /* returned by getpass() */
50         char **rdns;            /* for fiddling with the DN */
51         int authmethod;
52         int name_provided;      /* was a name passed in? */
53 #ifdef HAVE_GETPWUID
54         struct passwd *pw;      /* for getting user id */
55 #else
56         char *user;
57 #endif
58         char uidname[20];
59 #ifdef HAVE_KERBEROS
60         char **krbnames;        /* for kerberos names */
61         int kinited, ikrb;
62         char buf[5];
63         extern int krb_debug;
64 #endif
65         LDAPMessage *mp;        /* returned from find() */
66         static char prompt[MED_BUF_SIZE];       /* place for us to sprintf the prompt */
67         static char name[MED_BUF_SIZE]; /* place to store the user's name */
68         static char password[MED_BUF_SIZE];     /* password entered by user */
69
70 #ifdef DEBUG
71         if (debug & D_TRACE)
72                 fprintf(stderr, "auth(%s, NULL)\n", who);
73 #endif
74         name_provided = ( who != NULL );
75
76         /*
77          *  The user needs to bind.  If <who> is not specified, we
78          *  assume that authenticating as user id is what user wants.
79          */
80         if (who == NULL && implicit) {
81                 uidname[0] = '\0';
82
83 #ifdef HAVE_GETPWUID
84                 if ((pw = getpwuid((uid_t)geteuid())) != (struct passwd *) NULL) {
85                         sprintf(uidname, "uid=%s", pw->pw_name);
86                 }
87 #else
88                 user = getenv("USER");
89                 if(user == NULL) user = getenv("USERNAME");
90                 if(user == NULL) user = getenv("LOGNAME");
91
92                 if(user != NULL) {
93                         sprintf(uidname, "uid=%s", user);
94                 }
95 #endif
96
97                 if(uidname[0] != '\0') {
98                         who = uidname;
99                 }
100         }
101
102         if ( who == NULL ) {
103                 if ( implicit )
104                         printf( "You must first authenticate yourself to the Directory.\n" );
105 #ifdef UOFM
106                 printf("  What is your name or uniqname? ");
107 #else
108                 printf("  What is your name or user id? ");
109 #endif
110                 fflush(stdout);
111                 fetch_buffer(name, sizeof(name), stdin);
112                 if (name[0] == '\0')
113                         return( -1 );
114                 who = name;
115         }
116
117 #ifdef DEBUG
118         if (debug & D_AUTHENTICAT)
119                 printf("  Authenticating as \"%s\"\n", who);
120 #endif
121
122         /*
123          *  Bail out if the name is bogus.  If not, strip off the junk
124          *  at the start of the DN, build a prompt, and get a password 
125          *  from the user.  Then perform the ldap_bind().
126          */
127         if ((mp = find(who, TRUE)) == NULL) {
128                 printf("  I could not find \"%s\" in the Directory.\n", who);
129                 printf("  I used a search base of ");
130                 printbase("", search_base);
131                 printf("\n");
132 #ifdef DEBUG
133                 if (debug & D_AUTHENTICAT)
134                         printf("  Could not find \"%s\"\n", who);
135 #endif
136                 return(-1);
137         }
138
139         /*
140          *  Fill in the Entry structure.  May be handy later.
141          */
142         (void) parse_answer(mp);
143
144         rdns = ldap_explode_dn(Entry.DN, TRUE);
145         printf("  Authenticating to the directory as \"%s\"...\n", *rdns );
146
147 #ifdef HAVE_KERBEROS
148         /*
149          * First, if the user has a choice of auth methods, ask which
150          * one they want to use.  if they want kerberos, ask which
151          * krbname they want to bind as.
152          */
153
154         if ( (krbnames = ldap_get_values( ld, mp, "krbName" )) != NULL ) {
155                 int     choice, hassimple;
156
157                 hassimple = (ldap_compare_s( ld, Entry.DN, 
158                                 "userPassword", "x" ) == LDAP_COMPARE_FALSE);
159                 (void) ldap_msgfree(mp);
160
161                 /* if we're running as a server (e.g., out of inetd) */
162                 if ( ! isatty( 1 ) ) {
163                         strcpy( tktpath, "/tmp/ud_tktXXXXXX" );
164                         mktemp( tktpath );
165                         krb_set_tkt_string( tktpath );
166                 }
167
168                 kinited = valid_tgt( krbnames );
169
170                 if ( hassimple && !kinited ) {
171                         printf("  Which password would you like to use?\n");
172                         printf("    1 -> LDAP password\n");
173 #ifdef UOFM
174                         printf("    2 -> UMICH password (aka Uniqname or Kerberos password)\n");
175 #else
176                         printf("    2 -> Kerberos password\n");
177 #endif
178
179                         do {
180                                 printf("  Enter 1 or 2: ");
181                                 fflush(stdout);
182
183                                 fetch_buffer(buf, sizeof(buf), stdin);
184                                 choice = atoi(buf);
185                         } while (choice != 1 && choice != 2);
186
187                         authmethod = (choice == 1 ? LDAP_AUTH_SIMPLE :
188                             LDAP_AUTH_KRBV4);
189                 } else {
190                         authmethod = LDAP_AUTH_KRBV4;
191                 }
192         } else {
193                 authmethod = LDAP_AUTH_SIMPLE;
194                 (void) ldap_msgfree(mp);
195         }
196
197         /*
198          * if they are already kinited, we don't need to ask for a 
199          * password.
200          */
201
202         if ( authmethod == LDAP_AUTH_KRBV4 ) {
203                 if ( ! kinited ) {
204                         if ( krbnames[1] != NULL ) {
205                                 int     i;
206
207                                 /* ask which one to use */
208 #ifdef UOFM
209                                 printf("  Which UMICH (aka Kerberos or uniqname) name would you like to use?\n");
210 #else
211                                 printf("  Which Kerberos name would you like to use?\n");
212 #endif
213                                 for ( i = 0; krbnames[i] != NULL; i++ ) {
214                                         printf( "    %d -> %s\n", i + 1,
215                                             krbnames[i] );
216                                 }
217                                 do {
218                                         printf("  Enter a number between 1 and %d: ", i );
219                                         fflush( stdout );
220
221                                         fetch_buffer(buf, sizeof(buf), stdin);
222                                         ikrb = atoi(buf) - 1;
223                                 } while ( ikrb > i - 1 || ikrb < 0 );
224                         } else {
225                                 ikrb = 0;
226                         }
227
228                         /* kinit */
229                         if ( kinit( krbnames[ikrb] ) != 0 ) {
230                                 (void) ldap_value_free(rdns);
231                                 (void) ldap_value_free(krbnames);
232                                 return(-1);
233                         }
234                 }
235         } else {
236 #endif
237                 authmethod = LDAP_AUTH_SIMPLE;
238                 sprintf(prompt, "  Enter your LDAP password: ");
239                 do {
240                         passwd = getpass(prompt);
241                 } while (passwd != NULL && *passwd == '\0');
242                 if (passwd == NULL) {
243                         (void) ldap_value_free(rdns);
244                         return(0);
245                 }
246 #ifdef HAVE_KERBEROS
247         }
248         (void) ldap_value_free(krbnames);
249 #endif
250         ldap_flush_cache( ld );
251         rc = ldap_bind_s(ld, Entry.DN, passwd, authmethod);
252         if (rc != LDAP_SUCCESS) {
253                 int ld_errno;
254                 ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ld_errno);
255                 if (ld_errno == LDAP_NO_SUCH_ATTRIBUTE)
256                         fprintf(stderr, "  Entry has no password\n");
257                 else if (ld_errno == LDAP_INVALID_CREDENTIALS)
258 #ifdef HAVE_KERBEROS
259                         if ( authmethod == LDAP_AUTH_KRBV4 ) {
260                                 fprintf(stderr, "  The Kerberos credentials are invalid.\n");
261                         } else {
262 #endif
263                                 fprintf(stderr, "  The password you provided is incorrect.\n");
264 #ifdef HAVE_KERBEROS
265                         }
266 #endif
267                 else
268                         ldap_perror(ld, "ldap_bind_s" );
269                 (void) ldap_bind_s(ld, default_bind_object,
270                          (char *) NULL, LDAP_AUTH_SIMPLE);
271                 if (default_bind_object == NULL)
272                         set_bound_dn(NULL);
273                 else
274                         set_bound_dn(default_bind_object);
275                 bind_status = UD_NOT_BOUND;
276                 if (verbose)
277                         printf("  Authentication failed.\n\n");
278                 (void) ldap_value_free(rdns);
279                 return(-1);
280         }
281         else if (verbose)
282                 printf("  Authentication successful.\n\n");
283         else
284                 printf("\n");
285         set_bound_dn(Entry.DN);
286         bind_status = UD_BOUND;
287         if (passwd != NULL)
288                 (void) strcpy(password, passwd);
289         (void) ldap_value_free(rdns);
290         return(0);
291 }
292
293 #ifdef HAVE_KERBEROS
294
295 #define FIVEMINS        ( 5 * 60 )
296 #define TGT             "krbtgt"
297
298 static int
299 valid_tgt( char **names )
300 {
301         int             i;
302         char            name[ ANAME_SZ ], inst[ INST_SZ ], realm[ REALM_SZ ];
303         CREDENTIALS     cred;
304
305         for ( i = 0; names[i] != NULL; i++ ) {
306                 if ( kname_parse( name, inst, realm, names[i] ) != KSUCCESS ) {
307                         fprintf( stderr, "Bad format for krbName %s\n",
308                             names[i] );
309                         fprintf( stderr, "Contact x500@umich.edu\n" );
310                         return( 0 );
311                 }
312
313 #ifdef HAVE_AFS_KERBEROS
314                 /*
315                  * realm must be uppercase for krb_ routines
316                  */
317                 ldap_pvt_str2upper( realm );
318 #endif /* HAVE_AFS_KERBEROS */
319
320                 /*
321                 * check ticket file for a valid ticket granting ticket
322                 * my check is: have ticket granting ticket and it is good for
323                 * at least 5 more minutes
324                 */
325                 if ( krb_get_cred( TGT, realm, realm,
326                     &cred ) == KSUCCESS && time( 0 ) + FIVEMINS <
327                     cred.issue_date + (u_char)cred.lifetime * FIVEMINS ) {
328                         return( 1 );
329                 }
330         }
331
332         return( 0 );
333 }
334
335 static char *kauth_name;
336
337 /*ARGSUSED*/
338 int
339 krbgetpass( char *user, char *inst, char *realm, char *pw, C_Block key )
340 {
341         char    *p, lcrealm[ REALM_SZ ], prompt[256], *passwd;
342
343 #ifdef UOFM
344         sprintf(prompt, "  Enter the UMICH password (same as Uniqname or Kerberos password)\n  for %s: ", kauth_name );
345 #else
346         sprintf(prompt, "  Enter Kerberos password for %s: ", kauth_name );
347 #endif
348         do {
349                 passwd = getpass(prompt);
350         } while (passwd != NULL && *passwd == '\0');
351         if (passwd == NULL) {
352                 return(-1);
353         }
354
355 #ifdef HAVE_AFS_KERBEROS
356         strcpy( lcrealm, realm );
357         for ( p = lcrealm; *p != '\0'; ++p ) {
358                 *p = TOLOWER( (unsigned char) *p );
359         }
360
361         ka_StringToKey( passwd, lcrealm, key );
362 #else /* HAVE_AFS_KERBEROS */
363         string_to_key( passwd, key );
364 #endif /* HAVE_AFS_KERBEROS */
365
366         return( 0 );
367 }
368
369 static int
370 kinit( char *kname )
371 {
372         int     rc;
373         char    name[ ANAME_SZ ], inst[ INST_SZ ], realm[ REALM_SZ ];
374
375         kauth_name = kname;
376
377         if ( kname_parse( name, inst, realm, kname ) != KSUCCESS ) {
378                 fprintf( stderr, "Bad format for krbName %s\n",
379                     kname );
380                 fprintf( stderr, "Contact x500@umich.edu\n" );
381                 return( -1 );
382         }
383
384 #ifdef HAVE_AFS_KERBEROS
385         /*
386          * realm must be uppercase for krb_ routines
387          */
388         ldap_pvt_str2upper( realm );
389 #endif /* HAVE_AFS_KERBEROS */
390
391         rc = krb_get_in_tkt( name, inst, realm, TGT, realm,
392             DEFAULT_TKT_LIFE, krbgetpass, NULL, NULL );
393
394         if ( rc != KSUCCESS ) {
395                 switch ( rc ) {
396                 case SKDC_CANT:
397                         fprintf( stderr, "Can't contact Kerberos server for %s\n", realm );
398                         break;
399                 default:
400                         fprintf( stderr, "%s: %s\n", name, krb_err_txt[ rc ] );
401                         break;
402                 }
403                 return( -1 );
404         }
405
406         return( 0 );
407 }
408
409 void
410 destroy_tickets( void )
411 {
412         if ( *tktpath != '\0' ) {
413                 unlink( tktpath );
414         }
415 }
416 #endif
417
418 static void
419 set_bound_dn( char *s )
420 {
421         if (bound_dn != NULL)
422                 Free(bound_dn);
423         bound_dn = (s == NULL) ? NULL : strdup(s);
424 }