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