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