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