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