diff options
author | Colin Watson <cjwatson@debian.org> | 2005-09-14 11:27:01 +0000 |
---|---|---|
committer | Colin Watson <cjwatson@debian.org> | 2005-09-14 11:27:01 +0000 |
commit | 16704d57999d987fb8d9ba53379841a79f016d67 (patch) | |
tree | b7ecfbd5be83f191af382f3186c39ba1843ba7a1 /auth-krb4.c | |
parent | c8ab8ceacbe4dbdd7afea4e890d92e86282d050e (diff) | |
parent | a55bd782aa819b7f5ae716de000f19f4f531850e (diff) |
Import OpenSSH 4.2p1.
Diffstat (limited to 'auth-krb4.c')
-rw-r--r-- | auth-krb4.c | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/auth-krb4.c b/auth-krb4.c new file mode 100644 index 000000000..b28df469f --- /dev/null +++ b/auth-krb4.c | |||
@@ -0,0 +1,368 @@ | |||
1 | /* | ||
2 | * Copyright (c) 1999 Dug Song. All rights reserved. | ||
3 | * | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * 1. Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * 2. Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * | ||
13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
14 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
15 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
16 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
17 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
18 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
19 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
20 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
21 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
22 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
23 | */ | ||
24 | |||
25 | #include "includes.h" | ||
26 | RCSID("$OpenBSD: auth-krb4.c,v 1.29 2003/02/21 10:34:48 mpech Exp $"); | ||
27 | |||
28 | #include "ssh.h" | ||
29 | #include "ssh1.h" | ||
30 | #include "packet.h" | ||
31 | #include "xmalloc.h" | ||
32 | #include "log.h" | ||
33 | #include "servconf.h" | ||
34 | #include "uidswap.h" | ||
35 | #include "auth.h" | ||
36 | |||
37 | #ifdef AFS | ||
38 | #include "radix.h" | ||
39 | #endif | ||
40 | |||
41 | #ifdef KRB4 | ||
42 | extern ServerOptions options; | ||
43 | |||
44 | static int | ||
45 | krb4_init(void *context) | ||
46 | { | ||
47 | static int cleanup_registered = 0; | ||
48 | Authctxt *authctxt = (Authctxt *)context; | ||
49 | const char *tkt_root = TKT_ROOT; | ||
50 | struct stat st; | ||
51 | int fd; | ||
52 | |||
53 | if (!authctxt->krb4_ticket_file) { | ||
54 | /* Set unique ticket string manually since we're still root. */ | ||
55 | authctxt->krb4_ticket_file = xmalloc(MAXPATHLEN); | ||
56 | #ifdef AFS | ||
57 | if (lstat("/ticket", &st) != -1) | ||
58 | tkt_root = "/ticket/"; | ||
59 | #endif /* AFS */ | ||
60 | snprintf(authctxt->krb4_ticket_file, MAXPATHLEN, "%s%u_%ld", | ||
61 | tkt_root, authctxt->pw->pw_uid, (long)getpid()); | ||
62 | krb_set_tkt_string(authctxt->krb4_ticket_file); | ||
63 | } | ||
64 | /* Register ticket cleanup in case of fatal error. */ | ||
65 | if (!cleanup_registered) { | ||
66 | fatal_add_cleanup(krb4_cleanup_proc, authctxt); | ||
67 | cleanup_registered = 1; | ||
68 | } | ||
69 | /* Try to create our ticket file. */ | ||
70 | if ((fd = mkstemp(authctxt->krb4_ticket_file)) != -1) { | ||
71 | close(fd); | ||
72 | return (1); | ||
73 | } | ||
74 | /* Ticket file exists - make sure user owns it (just passed ticket). */ | ||
75 | if (lstat(authctxt->krb4_ticket_file, &st) != -1) { | ||
76 | if (st.st_mode == (S_IFREG | S_IRUSR | S_IWUSR) && | ||
77 | st.st_uid == authctxt->pw->pw_uid) | ||
78 | return (1); | ||
79 | } | ||
80 | /* Failure - cancel cleanup function, leaving ticket for inspection. */ | ||
81 | log("WARNING: bad ticket file %s", authctxt->krb4_ticket_file); | ||
82 | |||
83 | fatal_remove_cleanup(krb4_cleanup_proc, authctxt); | ||
84 | cleanup_registered = 0; | ||
85 | |||
86 | xfree(authctxt->krb4_ticket_file); | ||
87 | authctxt->krb4_ticket_file = NULL; | ||
88 | |||
89 | return (0); | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * try krb4 authentication, | ||
94 | * return 1 on success, 0 on failure, -1 if krb4 is not available | ||
95 | */ | ||
96 | int | ||
97 | auth_krb4_password(Authctxt *authctxt, const char *password) | ||
98 | { | ||
99 | AUTH_DAT adata; | ||
100 | KTEXT_ST tkt; | ||
101 | struct hostent *hp; | ||
102 | struct passwd *pw; | ||
103 | char localhost[MAXHOSTNAMELEN], phost[INST_SZ], realm[REALM_SZ]; | ||
104 | u_int32_t faddr; | ||
105 | int r; | ||
106 | |||
107 | if ((pw = authctxt->pw) == NULL) | ||
108 | return (0); | ||
109 | |||
110 | /* | ||
111 | * Try Kerberos password authentication only for non-root | ||
112 | * users and only if Kerberos is installed. | ||
113 | */ | ||
114 | if (pw->pw_uid != 0 && krb_get_lrealm(realm, 1) == KSUCCESS) { | ||
115 | /* Set up our ticket file. */ | ||
116 | if (!krb4_init(authctxt)) { | ||
117 | log("Couldn't initialize Kerberos ticket file for %s!", | ||
118 | pw->pw_name); | ||
119 | goto failure; | ||
120 | } | ||
121 | /* Try to get TGT using our password. */ | ||
122 | r = krb_get_pw_in_tkt((char *) pw->pw_name, "", realm, | ||
123 | "krbtgt", realm, DEFAULT_TKT_LIFE, (char *)password); | ||
124 | if (r != INTK_OK) { | ||
125 | debug("Kerberos v4 password authentication for %s " | ||
126 | "failed: %s", pw->pw_name, krb_err_txt[r]); | ||
127 | goto failure; | ||
128 | } | ||
129 | /* Successful authentication. */ | ||
130 | chown(tkt_string(), pw->pw_uid, pw->pw_gid); | ||
131 | |||
132 | /* | ||
133 | * Now that we have a TGT, try to get a local | ||
134 | * "rcmd" ticket to ensure that we are not talking | ||
135 | * to a bogus Kerberos server. | ||
136 | */ | ||
137 | gethostname(localhost, sizeof(localhost)); | ||
138 | strlcpy(phost, (char *)krb_get_phost(localhost), | ||
139 | sizeof(phost)); | ||
140 | r = krb_mk_req(&tkt, KRB4_SERVICE_NAME, phost, realm, 33); | ||
141 | |||
142 | if (r == KSUCCESS) { | ||
143 | if ((hp = gethostbyname(localhost)) == NULL) { | ||
144 | log("Couldn't get local host address!"); | ||
145 | goto failure; | ||
146 | } | ||
147 | memmove((void *)&faddr, (void *)hp->h_addr, | ||
148 | sizeof(faddr)); | ||
149 | |||
150 | /* Verify our "rcmd" ticket. */ | ||
151 | r = krb_rd_req(&tkt, KRB4_SERVICE_NAME, phost, | ||
152 | faddr, &adata, ""); | ||
153 | if (r == RD_AP_UNDEC) { | ||
154 | /* | ||
155 | * Probably didn't have a srvtab on | ||
156 | * localhost. Disallow login. | ||
157 | */ | ||
158 | log("Kerberos v4 TGT for %s unverifiable, " | ||
159 | "no srvtab installed? krb_rd_req: %s", | ||
160 | pw->pw_name, krb_err_txt[r]); | ||
161 | goto failure; | ||
162 | } else if (r != KSUCCESS) { | ||
163 | log("Kerberos v4 %s ticket unverifiable: %s", | ||
164 | KRB4_SERVICE_NAME, krb_err_txt[r]); | ||
165 | goto failure; | ||
166 | } | ||
167 | } else if (r == KDC_PR_UNKNOWN) { | ||
168 | /* | ||
169 | * Disallow login if no rcmd service exists, and | ||
170 | * log the error. | ||
171 | */ | ||
172 | log("Kerberos v4 TGT for %s unverifiable: %s; %s.%s " | ||
173 | "not registered, or srvtab is wrong?", pw->pw_name, | ||
174 | krb_err_txt[r], KRB4_SERVICE_NAME, phost); | ||
175 | goto failure; | ||
176 | } else { | ||
177 | /* | ||
178 | * TGT is bad, forget it. Possibly spoofed! | ||
179 | */ | ||
180 | debug("WARNING: Kerberos v4 TGT possibly spoofed " | ||
181 | "for %s: %s", pw->pw_name, krb_err_txt[r]); | ||
182 | goto failure; | ||
183 | } | ||
184 | /* Authentication succeeded. */ | ||
185 | return (1); | ||
186 | } else | ||
187 | /* Logging in as root or no local Kerberos realm. */ | ||
188 | debug("Unable to authenticate to Kerberos."); | ||
189 | |||
190 | failure: | ||
191 | krb4_cleanup_proc(authctxt); | ||
192 | |||
193 | if (!options.kerberos_or_local_passwd) | ||
194 | return (0); | ||
195 | |||
196 | /* Fall back to ordinary passwd authentication. */ | ||
197 | return (-1); | ||
198 | } | ||
199 | |||
200 | void | ||
201 | krb4_cleanup_proc(void *context) | ||
202 | { | ||
203 | Authctxt *authctxt = (Authctxt *)context; | ||
204 | debug("krb4_cleanup_proc called"); | ||
205 | if (authctxt->krb4_ticket_file) { | ||
206 | (void) dest_tkt(); | ||
207 | xfree(authctxt->krb4_ticket_file); | ||
208 | authctxt->krb4_ticket_file = NULL; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | int | ||
213 | auth_krb4(Authctxt *authctxt, KTEXT auth, char **client, KTEXT reply) | ||
214 | { | ||
215 | AUTH_DAT adat = {0}; | ||
216 | Key_schedule schedule; | ||
217 | struct sockaddr_in local, foreign; | ||
218 | char instance[INST_SZ]; | ||
219 | socklen_t slen; | ||
220 | u_int cksum; | ||
221 | int r, s; | ||
222 | |||
223 | s = packet_get_connection_in(); | ||
224 | |||
225 | slen = sizeof(local); | ||
226 | memset(&local, 0, sizeof(local)); | ||
227 | if (getsockname(s, (struct sockaddr *) & local, &slen) < 0) | ||
228 | debug("getsockname failed: %.100s", strerror(errno)); | ||
229 | slen = sizeof(foreign); | ||
230 | memset(&foreign, 0, sizeof(foreign)); | ||
231 | if (getpeername(s, (struct sockaddr *) & foreign, &slen) < 0) { | ||
232 | debug("getpeername failed: %.100s", strerror(errno)); | ||
233 | fatal_cleanup(); | ||
234 | } | ||
235 | instance[0] = '*'; | ||
236 | instance[1] = 0; | ||
237 | |||
238 | /* Get the encrypted request, challenge, and session key. */ | ||
239 | if ((r = krb_rd_req(auth, KRB4_SERVICE_NAME, instance, | ||
240 | 0, &adat, ""))) { | ||
241 | debug("Kerberos v4 krb_rd_req: %.100s", krb_err_txt[r]); | ||
242 | return (0); | ||
243 | } | ||
244 | des_key_sched((des_cblock *) adat.session, schedule); | ||
245 | |||
246 | *client = xmalloc(MAX_K_NAME_SZ); | ||
247 | (void) snprintf(*client, MAX_K_NAME_SZ, "%s%s%s@%s", adat.pname, | ||
248 | *adat.pinst ? "." : "", adat.pinst, adat.prealm); | ||
249 | |||
250 | /* Check ~/.klogin authorization now. */ | ||
251 | if (kuserok(&adat, authctxt->user) != KSUCCESS) { | ||
252 | log("Kerberos v4 .klogin authorization failed for %s to " | ||
253 | "account %s", *client, authctxt->user); | ||
254 | xfree(*client); | ||
255 | *client = NULL; | ||
256 | return (0); | ||
257 | } | ||
258 | /* Increment the checksum, and return it encrypted with the | ||
259 | session key. */ | ||
260 | cksum = adat.checksum + 1; | ||
261 | cksum = htonl(cksum); | ||
262 | |||
263 | /* If we can't successfully encrypt the checksum, we send back an | ||
264 | empty message, admitting our failure. */ | ||
265 | if ((r = krb_mk_priv((u_char *) & cksum, reply->dat, sizeof(cksum) + 1, | ||
266 | schedule, &adat.session, &local, &foreign)) < 0) { | ||
267 | debug("Kerberos v4 mk_priv: (%d) %s", r, krb_err_txt[r]); | ||
268 | reply->dat[0] = 0; | ||
269 | reply->length = 0; | ||
270 | } else | ||
271 | reply->length = r; | ||
272 | |||
273 | /* Clear session key. */ | ||
274 | memset(&adat.session, 0, sizeof(adat.session)); | ||
275 | return (1); | ||
276 | } | ||
277 | #endif /* KRB4 */ | ||
278 | |||
279 | #ifdef AFS | ||
280 | int | ||
281 | auth_krb4_tgt(Authctxt *authctxt, const char *string) | ||
282 | { | ||
283 | CREDENTIALS creds; | ||
284 | struct passwd *pw; | ||
285 | |||
286 | if ((pw = authctxt->pw) == NULL) | ||
287 | goto failure; | ||
288 | |||
289 | temporarily_use_uid(pw); | ||
290 | |||
291 | if (!radix_to_creds(string, &creds)) { | ||
292 | log("Protocol error decoding Kerberos v4 TGT"); | ||
293 | goto failure; | ||
294 | } | ||
295 | if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ | ||
296 | strlcpy(creds.service, "krbtgt", sizeof creds.service); | ||
297 | |||
298 | if (strcmp(creds.service, "krbtgt")) { | ||
299 | log("Kerberos v4 TGT (%s%s%s@%s) rejected for %s", | ||
300 | creds.pname, creds.pinst[0] ? "." : "", creds.pinst, | ||
301 | creds.realm, pw->pw_name); | ||
302 | goto failure; | ||
303 | } | ||
304 | if (!krb4_init(authctxt)) | ||
305 | goto failure; | ||
306 | |||
307 | if (in_tkt(creds.pname, creds.pinst) != KSUCCESS) | ||
308 | goto failure; | ||
309 | |||
310 | if (save_credentials(creds.service, creds.instance, creds.realm, | ||
311 | creds.session, creds.lifetime, creds.kvno, &creds.ticket_st, | ||
312 | creds.issue_date) != KSUCCESS) { | ||
313 | debug("Kerberos v4 TGT refused: couldn't save credentials"); | ||
314 | goto failure; | ||
315 | } | ||
316 | /* Successful authentication, passed all checks. */ | ||
317 | chown(tkt_string(), pw->pw_uid, pw->pw_gid); | ||
318 | |||
319 | debug("Kerberos v4 TGT accepted (%s%s%s@%s)", | ||
320 | creds.pname, creds.pinst[0] ? "." : "", creds.pinst, creds.realm); | ||
321 | memset(&creds, 0, sizeof(creds)); | ||
322 | |||
323 | restore_uid(); | ||
324 | |||
325 | return (1); | ||
326 | |||
327 | failure: | ||
328 | krb4_cleanup_proc(authctxt); | ||
329 | memset(&creds, 0, sizeof(creds)); | ||
330 | restore_uid(); | ||
331 | |||
332 | return (0); | ||
333 | } | ||
334 | |||
335 | int | ||
336 | auth_afs_token(Authctxt *authctxt, const char *token_string) | ||
337 | { | ||
338 | CREDENTIALS creds; | ||
339 | struct passwd *pw; | ||
340 | uid_t uid; | ||
341 | |||
342 | if ((pw = authctxt->pw) == NULL) | ||
343 | return (0); | ||
344 | |||
345 | if (!radix_to_creds(token_string, &creds)) { | ||
346 | log("Protocol error decoding AFS token"); | ||
347 | return (0); | ||
348 | } | ||
349 | if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ | ||
350 | strlcpy(creds.service, "afs", sizeof creds.service); | ||
351 | |||
352 | if (strncmp(creds.pname, "AFS ID ", 7) == 0) | ||
353 | uid = atoi(creds.pname + 7); | ||
354 | else | ||
355 | uid = pw->pw_uid; | ||
356 | |||
357 | if (kafs_settoken(creds.realm, uid, &creds)) { | ||
358 | log("AFS token (%s@%s) rejected for %s", | ||
359 | creds.pname, creds.realm, pw->pw_name); | ||
360 | memset(&creds, 0, sizeof(creds)); | ||
361 | return (0); | ||
362 | } | ||
363 | debug("AFS token accepted (%s@%s)", creds.pname, creds.realm); | ||
364 | memset(&creds, 0, sizeof(creds)); | ||
365 | |||
366 | return (1); | ||
367 | } | ||
368 | #endif /* AFS */ | ||