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