From http://www.jwz.org/xscreensaver/xscreensaver-5.16.tar.gz
[xscreensaver] / driver / passwd-kerberos.c
1 /* kpasswd.c --- verify kerberos passwords.
2  * written by Nat Lanza (magus@cs.cmu.edu) for
3  * xscreensaver, Copyright (c) 1993-2004 Jamie Zawinski <jwz@jwz.org>
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that
8  * copyright notice and this permission notice appear in supporting
9  * documentation.  No representations are made about the suitability of this
10  * software for any purpose.  It is provided "as is" without express or 
11  * implied warranty.
12  */
13
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
17
18 #ifndef NO_LOCKING  /* whole file */
19
20 #include <stdlib.h>
21 #ifdef HAVE_UNISTD_H
22 # include <unistd.h>
23 #endif
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29
30 /* I'm not sure if this is exactly the right test...
31    Might __APPLE__ be defined if this is apple hardware, but not
32    an Apple OS?
33
34    Thanks to Alexei Kosut <akosut@stanford.edu> for the MacOS X code.
35  */
36 #ifdef __APPLE__
37 # define HAVE_DARWIN
38 #endif
39
40
41 #if defined(HAVE_DARWIN)
42 # include <Kerberos/Kerberos.h>
43 #elif defined(HAVE_KERBEROS5)
44 # include <kerberosIV/krb.h>
45 # include <kerberosIV/des.h>
46 #else /* !HAVE_KERBEROS5 (meaning Kerberos 4) */
47 # include <krb.h>
48 # include <des.h>
49 #endif /* !HAVE_KERBEROS5 */
50
51 #if !defined(VMS) && !defined(HAVE_ADJUNCT_PASSWD)
52 # include <pwd.h>
53 #endif
54
55
56 #ifdef __bsdi__
57 # include <sys/param.h>
58 # if _BSDI_VERSION >= 199608
59 #  define BSD_AUTH
60 # endif
61 #endif /* __bsdi__ */
62
63 /* blargh */
64 #undef  Bool
65 #undef  True
66 #undef  False
67 #define Bool  int
68 #define True  1
69 #define False 0
70
71 /* The user information we need to store */
72 #ifdef HAVE_DARWIN
73  static KLPrincipal princ;
74 #else /* !HAVE_DARWIN */
75  static char realm[REALM_SZ];
76  static char  name[ANAME_SZ];
77  static char  inst[INST_SZ];
78  static const char *tk_file;
79 #endif /* !HAVE_DARWIN */
80
81 /* warning suppression: duplicated in passwd.c */
82 extern Bool kerberos_lock_init (int argc, char **argv, Bool verbose_p);
83 extern Bool kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p);
84
85
86 /* Called at startup to grab user, instance, and realm information
87    from the user's ticketfile (remember, name.inst@realm). Since we're
88    using tf_get_pname(), this should work even if your kerberos username
89    isn't the same as your local username. We grab the ticket at startup
90    time so that even if your ticketfile dies while the screen's locked
91    we'll still have the information to unlock it.
92
93    Problems: the password dialog currently displays local username, so if
94      you have some non-standard name/instance when you run xscreensaver,
95      you'll need to remember what it was when unlocking, or else you lose.
96
97      Also, we use des_string_to_key(), so if you have an AFS password
98      (encrypted with ka_StringToKey()), you'll lose. Get a kerberos password;
99      it isn't that hard.
100
101    Like the original lock_init, we return false if something went wrong.
102    We don't use the arguments we're given, though.
103  */
104 Bool
105 kerberos_lock_init (int argc, char **argv, Bool verbose_p)
106 {
107 # ifdef HAVE_DARWIN
108
109     KLBoolean found;
110     return ((klNoErr == (KLCacheHasValidTickets (NULL, kerberosVersion_Any,
111                                                  &found, &princ, NULL)))
112             && found);
113
114 # else /* !HAVE_DARWIN */
115
116     /* Perhaps we should be doing it the Mac way (above) all the time?
117        The following code assumes Unix-style file-based Kerberos credentials
118        cache, which Mac OS X doesn't use.  But is there any real reason to
119        do it this way at all, even on other Unixen?
120      */
121     int k_errno;
122     
123     memset(name, 0, sizeof(name));
124     memset(inst, 0, sizeof(inst));
125     
126     /* find out where the user's keeping his tickets.
127        squirrel it away for later use. */
128     tk_file = tkt_string();
129
130     /* open ticket file or die trying. */
131     if ((k_errno = tf_init(tk_file, R_TKT_FIL))) {
132         return False;
133     }
134
135     /* same with principal and instance names */
136     if ((k_errno = tf_get_pname(name)) ||
137         (k_errno = tf_get_pinst(inst))) {
138         return False;
139     }
140
141     /* close the ticketfile to release the lock on it. */
142     tf_close();
143
144     /* figure out what realm we're authenticated to. this ought
145        to be the local realm, but it pays to be sure. */
146     if ((k_errno = krb_get_tf_realm(tk_file, realm))) {
147         return False;
148     }
149
150     /* last-minute sanity check on what we got. */
151     if ((strlen(name)+strlen(inst)+strlen(realm)+3) >
152         (REALM_SZ + ANAME_SZ + INST_SZ + 3)) {
153         return False;
154     }
155
156     /* success */
157     return True;
158
159 # endif /* !HAVE_DARWIN */
160 }
161
162
163 /* des_string_to_key() wants this. If C didn't suck, we could have an
164    anonymous function do this. Even a local one. But it does, so here
165    we are. Calling it ive_got_your_local_function_right_here_buddy()
166    would have been rude.
167  */
168 #ifndef HAVE_DARWIN
169 static int 
170 key_to_key(char *user, char *instance, char *realm, char *passwd, C_Block key)
171 {
172   memcpy(key, passwd, sizeof(des_cblock));
173   return (0);
174 }
175 #endif /* !HAVE_DARWIN */
176
177 /* Called to see if the user's typed password is valid. We do this by asking
178    the kerberos server for a ticket and checking to see if it gave us one.
179    We need to move the ticketfile first, or otherwise we end up updating the
180    user's tkfile with new tickets. This would break services like zephyr that
181    like to stay authenticated, and it would screw with AFS authentication at
182    some sites. So, we do a quick, painful hack with a tmpfile.
183  */
184 Bool
185 kerberos_passwd_valid_p (const char *typed_passwd, Bool verbose_p)
186 {
187 # ifdef HAVE_DARWIN
188     return (klNoErr ==
189             KLAcquireNewInitialTicketsWithPassword (princ, NULL,
190                                                     typed_passwd, NULL));
191 # else /* !HAVE_DARWIN */
192
193     /* See comments in kerberos_lock_init -- should we do it the Mac Way
194        on all systems?
195      */
196     C_Block mitkey;
197     Bool success;
198     char *newtkfile;
199     int fh = -1;
200
201     /* temporarily switch to a new ticketfile.
202        I'm not using tmpnam() because it isn't entirely portable.
203        this could probably be fixed with autoconf. */
204     newtkfile = malloc(80 * sizeof(char));
205     memset(newtkfile, 0, sizeof(newtkfile));
206
207     sprintf(newtkfile, "/tmp/xscrn-%i.XXXXXX", getpid());
208
209     if( (fh = mkstemp(newtkfile)) < 0)
210     {
211         free(newtkfile);
212         return(False);
213     }
214     if( fchmod(fh, 0600) < 0)
215     {
216         free(newtkfile);
217         return(False);
218     }
219
220
221     krb_set_tkt_string(newtkfile);
222
223     /* encrypt the typed password. if you have an AFS password instead
224        of a kerberos one, you lose *right here*. If you want to use AFS
225        passwords, you can use ka_StringToKey() instead. As always, ymmv. */
226     des_string_to_key(typed_passwd, mitkey);
227
228     if (krb_get_in_tkt(name, inst, realm, "krbtgt", realm, DEFAULT_TKT_LIFE,
229                        key_to_key, NULL, (char *) mitkey) != 0) {
230         success = False;
231     } else {
232         success = True;
233     }
234
235     /* quickly block out the tempfile and password to prevent snooping,
236        then restore the old ticketfile and cleean up a bit. */
237     
238     dest_tkt();
239     krb_set_tkt_string(tk_file);
240     free(newtkfile);
241     memset(mitkey, 0, sizeof(mitkey));
242     close(fh); /* #### tom: should the file be removed? */
243     
244
245     /* Did we verify successfully? */
246     return success;
247
248 # endif /* !HAVE_DARWIN */
249 }
250
251 #endif /* NO_LOCKING -- whole file */