]> git.sur5r.net Git - openldap/blob - clients/ud/auth.c
Protoized, moved extern definitions to .h files, fixed related bugs.
[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 #include <ac/unistd.h>
23 extern char *strdup (const char *);
24
25 #ifdef HAVE_PWD_H
26 #include <pwd.h>
27 #endif
28
29 #include <lber.h>
30 #include <ldap.h>
31 #include <ldapconfig.h>
32
33 #include "ud.h"
34
35 #ifdef HAVE_KERBEROS
36 static char tktpath[20];        /* ticket file path */
37 static int kinit();
38 static int valid_tgt();
39 #endif
40
41 static void set_bound_dn(char *s);
42
43
44 int
45 auth( char *who, int implicit )
46 {
47         int rc;                 /* return code from ldap_bind() */
48         char *passwd = NULL;    /* returned by mygetpass() */
49         char **rdns;            /* for fiddling with the DN */
50         int authmethod;
51         int name_provided;      /* was a name passed in? */
52 #ifdef HAVE_GETPWUID
53         struct passwd *pw;      /* for getting user id */
54 #else
55         char *user;
56 #endif
57         char uidname[20];
58 #ifdef HAVE_KERBEROS
59         char **krbnames;        /* for kerberos names */
60         int kinited, ikrb;
61         char buf[5];
62         extern int krb_debug;
63 #endif
64         LDAPMessage *mp;        /* returned from find() */
65         static char prompt[MED_BUF_SIZE];       /* place for us to sprintf the prompt */
66         static char name[MED_BUF_SIZE]; /* place to store the user's name */
67         static char password[MED_BUF_SIZE];     /* password entered by user */
68
69 #ifdef DEBUG
70         if (debug & D_TRACE)
71                 fprintf(stderr, "auth(%s, NULL)\n", who);
72 #endif
73         name_provided = ( who != NULL );
74
75         /*
76          *  The user needs to bind.  If <who> is not specified, we
77          *  assume that authenticating as user id is what user wants.
78          */
79         if (who == NULL && implicit) {
80                 uidname[0] = '\0';
81
82 #ifdef HAVE_GETPWUID
83                 if ((pw = getpwuid((uid_t)geteuid())) != (struct passwd *) NULL) {
84                         sprintf(uidname, "uid=%s", pw->pw_name);
85                 }
86 #else
87                 user = getenv("USER");
88                 if(user == NULL) user = getenv("USERNAME");
89                 if(user == NULL) user = getenv("LOGNAME");
90
91                 if(user != NULL) {
92                         sprintf(uidname, "uid=%s", user);
93                 }
94 #endif
95
96                 if(uidname[0] != '\0') {
97                         who = uidname;
98                 }
99         }
100
101         if ( who == NULL ) {
102                 if ( implicit )
103                         printf( "You must first authenticate yourself to the Directory.\n" );
104 #ifdef UOFM
105                 printf("  What is your name or uniqname? ");
106 #else
107                 printf("  What is your name or user id? ");
108 #endif
109                 fflush(stdout);
110                 fetch_buffer(name, sizeof(name), stdin);
111                 if (name[0] == '\0')
112                         return( -1 );
113                 who = name;
114         }
115
116 #ifdef DEBUG
117         if (debug & D_AUTHENTICAT)
118                 printf("  Authenticating as \"%s\"\n", who);
119 #endif
120
121         /*
122          *  Bail out if the name is bogus.  If not, strip off the junk
123          *  at the start of the DN, build a prompt, and get a password 
124          *  from the user.  Then perform the ldap_bind().
125          */
126         if ((mp = find(who, TRUE)) == NULL) {
127                 (void) ldap_msgfree(mp);
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 = mygetpass(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 void
299 str2upper( char *s )
300 {
301         char    *p;
302
303         for ( p = s; *p != '\0'; ++p ) {
304                 *p = TOUPPER( *p );
305         }
306 }
307
308
309 static int
310 valid_tgt( char **names )
311 {
312         int             i;
313         char            name[ ANAME_SZ ], inst[ INST_SZ ], realm[ REALM_SZ ];
314         CREDENTIALS     cred;
315
316         for ( i = 0; names[i] != NULL; i++ ) {
317                 if ( kname_parse( name, inst, realm, names[i] ) != KSUCCESS ) {
318                         fprintf( stderr, "Bad format for krbName %s\n",
319                             names[i] );
320                         fprintf( stderr, "Contact x500@umich.edu\n" );
321                         return( 0 );
322                 }
323
324 #ifdef HAVE_AFS_KERBEROS
325                 /*
326                  * realm must be uppercase for krb_ routines
327                  */
328                 str2upper( realm );
329 #endif /* HAVE_AFS_KERBEROS */
330
331                 /*
332                 * check ticket file for a valid ticket granting ticket
333                 * my check is: have ticket granting ticket and it is good for
334                 * at least 5 more minutes
335                 */
336                 if ( krb_get_cred( TGT, realm, realm,
337                     &cred ) == KSUCCESS && time( 0 ) + FIVEMINS <
338                     cred.issue_date + (u_char)cred.lifetime * FIVEMINS ) {
339                         return( 1 );
340                 }
341         }
342
343         return( 0 );
344 }
345
346 static char *kauth_name;
347
348 /*ARGSUSED*/
349 int
350 krbgetpass( char *user, char *inst, char *realm, char *pw, C_Block key )
351 {
352         char    *p, lcrealm[ REALM_SZ ], prompt[256], *passwd;
353
354 #ifdef UOFM
355         sprintf(prompt, "  Enter the UMICH password (same as Uniqname or Kerberos password)\n  for %s: ", kauth_name );
356 #else
357         sprintf(prompt, "  Enter Kerberos password for %s: ", kauth_name );
358 #endif
359         do {
360                 passwd = mygetpass(prompt);
361         } while (passwd != NULL && *passwd == '\0');
362         if (passwd == NULL) {
363                 return(-1);
364         }
365
366 #ifdef HAVE_AFS_KERBEROS
367         strcpy( lcrealm, realm );
368         for ( p = lcrealm; *p != '\0'; ++p ) {
369                 *p = TOLOWER( *p );
370         }
371
372         ka_StringToKey( passwd, lcrealm, key );
373 #else /* HAVE_AFS_KERBEROS */
374         string_to_key( passwd, key );
375 #endif /* HAVE_AFS_KERBEROS */
376
377         return( 0 );
378 }
379
380 static int
381 kinit( char *kname )
382 {
383         int     rc;
384         char    name[ ANAME_SZ ], inst[ INST_SZ ], realm[ REALM_SZ ];
385
386         kauth_name = kname;
387
388         if ( kname_parse( name, inst, realm, kname ) != KSUCCESS ) {
389                 fprintf( stderr, "Bad format for krbName %s\n",
390                     kname );
391                 fprintf( stderr, "Contact x500@umich.edu\n" );
392                 return( -1 );
393         }
394
395 #ifdef HAVE_AFS_KERBEROS
396         /*
397          * realm must be uppercase for krb_ routines
398          */
399         str2upper( realm );
400 #endif /* HAVE_AFS_KERBEROS */
401
402         rc = krb_get_in_tkt( name, inst, realm, TGT, realm,
403             DEFAULT_TKT_LIFE, krbgetpass, NULL, NULL );
404
405         if ( rc != KSUCCESS ) {
406                 switch ( rc ) {
407                 case SKDC_CANT:
408                         fprintf( stderr, "Can't contact Kerberos server for %s\n", realm );
409                         break;
410                 default:
411                         fprintf( stderr, "%s: %s\n", name, krb_err_txt[ rc ] );
412                         break;
413                 }
414                 return( -1 );
415         }
416
417         return( 0 );
418 }
419
420 void
421 destroy_tickets( void )
422 {
423         if ( *tktpath != '\0' ) {
424                 unlink( tktpath );
425         }
426 }
427 #endif
428
429 static void
430 set_bound_dn( char *s )
431 {
432         if (bound_dn != NULL)
433                 Free(bound_dn);
434         bound_dn = (s == NULL) ? NULL : strdup(s);
435 }