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.28 2002/09/26 11:38:43 markus Exp $"); 27 28 #pragma ident "%Z%%M% %I% %E% SMI" 29 30 #include "ssh.h" 31 #include "ssh1.h" 32 #include "packet.h" 33 #include "xmalloc.h" 34 #include "log.h" 35 #include "servconf.h" 36 #include "uidswap.h" 37 #include "auth.h" 38 39 #ifdef AFS 40 #include "radix.h" 41 #endif 42 43 #ifdef KRB4 44 extern ServerOptions options; 45 46 static int 47 krb4_init(void *context) 48 { 49 static int cleanup_registered = 0; 50 Authctxt *authctxt = (Authctxt *)context; 51 const char *tkt_root = TKT_ROOT; 52 struct stat st; 53 int fd; 54 55 if (!authctxt->krb4_ticket_file) { 56 /* Set unique ticket string manually since we're still root. */ 57 authctxt->krb4_ticket_file = xmalloc(MAXPATHLEN); 58 #ifdef AFS 59 if (lstat("/ticket", &st) != -1) 60 tkt_root = "/ticket/"; 61 #endif /* AFS */ 62 snprintf(authctxt->krb4_ticket_file, MAXPATHLEN, "%s%u_%ld", 63 tkt_root, authctxt->pw->pw_uid, (long)getpid()); 64 krb_set_tkt_string(authctxt->krb4_ticket_file); 65 } 66 /* Register ticket cleanup in case of fatal error. */ 67 if (!cleanup_registered) { 68 fatal_add_cleanup(krb4_cleanup_proc, authctxt); 69 cleanup_registered = 1; 70 } 71 /* Try to create our ticket file. */ 72 if ((fd = mkstemp(authctxt->krb4_ticket_file)) != -1) { 73 close(fd); 74 return (1); 75 } 76 /* Ticket file exists - make sure user owns it (just passed ticket). */ 77 if (lstat(authctxt->krb4_ticket_file, &st) != -1) { 78 if (st.st_mode == (S_IFREG | S_IRUSR | S_IWUSR) && 79 st.st_uid == authctxt->pw->pw_uid) 80 return (1); 81 } 82 /* Failure - cancel cleanup function, leaving ticket for inspection. */ 83 log("WARNING: bad ticket file %s", authctxt->krb4_ticket_file); 84 85 fatal_remove_cleanup(krb4_cleanup_proc, authctxt); 86 cleanup_registered = 0; 87 88 xfree(authctxt->krb4_ticket_file); 89 authctxt->krb4_ticket_file = NULL; 90 91 return (0); 92 } 93 94 /* 95 * try krb4 authentication, 96 * return 1 on success, 0 on failure, -1 if krb4 is not available 97 */ 98 int 99 auth_krb4_password(Authctxt *authctxt, const char *password) 100 { 101 AUTH_DAT adata; 102 KTEXT_ST tkt; 103 struct hostent *hp; 104 struct passwd *pw; 105 char localhost[MAXHOSTNAMELEN], phost[INST_SZ], realm[REALM_SZ]; 106 u_int32_t faddr; 107 int r; 108 109 if ((pw = authctxt->pw) == NULL) 110 return (0); 111 112 /* 113 * Try Kerberos password authentication only for non-root 114 * users and only if Kerberos is installed. 115 */ 116 if (pw->pw_uid != 0 && krb_get_lrealm(realm, 1) == KSUCCESS) { 117 /* Set up our ticket file. */ 118 if (!krb4_init(authctxt)) { 119 log("Couldn't initialize Kerberos ticket file for %s!", 120 pw->pw_name); 121 goto failure; 122 } 123 /* Try to get TGT using our password. */ 124 r = krb_get_pw_in_tkt((char *) pw->pw_name, "", realm, 125 "krbtgt", realm, DEFAULT_TKT_LIFE, (char *)password); 126 if (r != INTK_OK) { 127 debug("Kerberos v4 password authentication for %s " 128 "failed: %s", pw->pw_name, krb_err_txt[r]); 129 goto failure; 130 } 131 /* Successful authentication. */ 132 chown(tkt_string(), pw->pw_uid, pw->pw_gid); 133 134 /* 135 * Now that we have a TGT, try to get a local 136 * "rcmd" ticket to ensure that we are not talking 137 * to a bogus Kerberos server. 138 */ 139 gethostname(localhost, sizeof(localhost)); 140 strlcpy(phost, (char *)krb_get_phost(localhost), 141 sizeof(phost)); 142 r = krb_mk_req(&tkt, KRB4_SERVICE_NAME, phost, realm, 33); 143 144 if (r == KSUCCESS) { 145 if ((hp = gethostbyname(localhost)) == NULL) { 146 log("Couldn't get local host address!"); 147 goto failure; 148 } 149 memmove((void *)&faddr, (void *)hp->h_addr, 150 sizeof(faddr)); 151 152 /* Verify our "rcmd" ticket. */ 153 r = krb_rd_req(&tkt, KRB4_SERVICE_NAME, phost, 154 faddr, &adata, ""); 155 if (r == RD_AP_UNDEC) { 156 /* 157 * Probably didn't have a srvtab on 158 * localhost. Disallow login. 159 */ 160 log("Kerberos v4 TGT for %s unverifiable, " 161 "no srvtab installed? krb_rd_req: %s", 162 pw->pw_name, krb_err_txt[r]); 163 goto failure; 164 } else if (r != KSUCCESS) { 165 log("Kerberos v4 %s ticket unverifiable: %s", 166 KRB4_SERVICE_NAME, krb_err_txt[r]); 167 goto failure; 168 } 169 } else if (r == KDC_PR_UNKNOWN) { 170 /* 171 * Disallow login if no rcmd service exists, and 172 * log the error. 173 */ 174 log("Kerberos v4 TGT for %s unverifiable: %s; %s.%s " 175 "not registered, or srvtab is wrong?", pw->pw_name, 176 krb_err_txt[r], KRB4_SERVICE_NAME, phost); 177 goto failure; 178 } else { 179 /* 180 * TGT is bad, forget it. Possibly spoofed! 181 */ 182 debug("WARNING: Kerberos v4 TGT possibly spoofed " 183 "for %s: %s", pw->pw_name, krb_err_txt[r]); 184 goto failure; 185 } 186 /* Authentication succeeded. */ 187 return (1); 188 } else 189 /* Logging in as root or no local Kerberos realm. */ 190 debug("Unable to authenticate to Kerberos."); 191 192 failure: 193 krb4_cleanup_proc(authctxt); 194 195 if (!options.kerberos_or_local_passwd) 196 return (0); 197 198 /* Fall back to ordinary passwd authentication. */ 199 return (-1); 200 } 201 202 void 203 krb4_cleanup_proc(void *context) 204 { 205 Authctxt *authctxt = (Authctxt *)context; 206 debug("krb4_cleanup_proc called"); 207 if (authctxt->krb4_ticket_file) { 208 (void) dest_tkt(); 209 xfree(authctxt->krb4_ticket_file); 210 authctxt->krb4_ticket_file = NULL; 211 } 212 } 213 214 int 215 auth_krb4(Authctxt *authctxt, KTEXT auth, char **client, KTEXT reply) 216 { 217 AUTH_DAT adat = {0}; 218 Key_schedule schedule; 219 struct sockaddr_in local, foreign; 220 char instance[INST_SZ]; 221 socklen_t slen; 222 u_int cksum; 223 int r, s; 224 225 s = packet_get_connection_in(); 226 227 slen = sizeof(local); 228 memset(&local, 0, sizeof(local)); 229 if (getsockname(s, (struct sockaddr *) & local, &slen) < 0) 230 debug("getsockname failed: %.100s", strerror(errno)); 231 slen = sizeof(foreign); 232 memset(&foreign, 0, sizeof(foreign)); 233 if (getpeername(s, (struct sockaddr *) & foreign, &slen) < 0) { 234 debug("getpeername failed: %.100s", strerror(errno)); 235 fatal_cleanup(); 236 } 237 instance[0] = '*'; 238 instance[1] = 0; 239 240 /* Get the encrypted request, challenge, and session key. */ 241 if ((r = krb_rd_req(auth, KRB4_SERVICE_NAME, instance, 242 0, &adat, ""))) { 243 debug("Kerberos v4 krb_rd_req: %.100s", krb_err_txt[r]); 244 return (0); 245 } 246 des_key_sched((des_cblock *) adat.session, schedule); 247 248 *client = xmalloc(MAX_K_NAME_SZ); 249 (void) snprintf(*client, MAX_K_NAME_SZ, "%s%s%s@%s", adat.pname, 250 *adat.pinst ? "." : "", adat.pinst, adat.prealm); 251 252 /* Check ~/.klogin authorization now. */ 253 if (kuserok(&adat, authctxt->user) != KSUCCESS) { 254 log("Kerberos v4 .klogin authorization failed for %s to " 255 "account %s", *client, authctxt->user); 256 xfree(*client); 257 *client = NULL; 258 return (0); 259 } 260 /* Increment the checksum, and return it encrypted with the 261 session key. */ 262 cksum = adat.checksum + 1; 263 cksum = htonl(cksum); 264 265 /* If we can't successfully encrypt the checksum, we send back an 266 empty message, admitting our failure. */ 267 if ((r = krb_mk_priv((u_char *) & cksum, reply->dat, sizeof(cksum) + 1, 268 schedule, &adat.session, &local, &foreign)) < 0) { 269 debug("Kerberos v4 mk_priv: (%d) %s", r, krb_err_txt[r]); 270 reply->dat[0] = 0; 271 reply->length = 0; 272 } else 273 reply->length = r; 274 275 /* Clear session key. */ 276 memset(&adat.session, 0, sizeof(&adat.session)); 277 return (1); 278 } 279 #endif /* KRB4 */ 280 281 #ifdef AFS 282 int 283 auth_krb4_tgt(Authctxt *authctxt, const char *string) 284 { 285 CREDENTIALS creds; 286 struct passwd *pw; 287 288 if ((pw = authctxt->pw) == NULL) 289 goto failure; 290 291 temporarily_use_uid(pw); 292 293 if (!radix_to_creds(string, &creds)) { 294 log("Protocol error decoding Kerberos v4 TGT"); 295 goto failure; 296 } 297 if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ 298 strlcpy(creds.service, "krbtgt", sizeof creds.service); 299 300 if (strcmp(creds.service, "krbtgt")) { 301 log("Kerberos v4 TGT (%s%s%s@%s) rejected for %s", 302 creds.pname, creds.pinst[0] ? "." : "", creds.pinst, 303 creds.realm, pw->pw_name); 304 goto failure; 305 } 306 if (!krb4_init(authctxt)) 307 goto failure; 308 309 if (in_tkt(creds.pname, creds.pinst) != KSUCCESS) 310 goto failure; 311 312 if (save_credentials(creds.service, creds.instance, creds.realm, 313 creds.session, creds.lifetime, creds.kvno, &creds.ticket_st, 314 creds.issue_date) != KSUCCESS) { 315 debug("Kerberos v4 TGT refused: couldn't save credentials"); 316 goto failure; 317 } 318 /* Successful authentication, passed all checks. */ 319 chown(tkt_string(), pw->pw_uid, pw->pw_gid); 320 321 debug("Kerberos v4 TGT accepted (%s%s%s@%s)", 322 creds.pname, creds.pinst[0] ? "." : "", creds.pinst, creds.realm); 323 memset(&creds, 0, sizeof(creds)); 324 325 restore_uid(); 326 327 return (1); 328 329 failure: 330 krb4_cleanup_proc(authctxt); 331 memset(&creds, 0, sizeof(creds)); 332 restore_uid(); 333 334 return (0); 335 } 336 337 int 338 auth_afs_token(Authctxt *authctxt, const char *token_string) 339 { 340 CREDENTIALS creds; 341 struct passwd *pw; 342 uid_t uid; 343 344 if ((pw = authctxt->pw) == NULL) 345 return (0); 346 347 if (!radix_to_creds(token_string, &creds)) { 348 log("Protocol error decoding AFS token"); 349 return (0); 350 } 351 if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ 352 strlcpy(creds.service, "afs", sizeof creds.service); 353 354 if (strncmp(creds.pname, "AFS ID ", 7) == 0) 355 uid = atoi(creds.pname + 7); 356 else 357 uid = pw->pw_uid; 358 359 if (kafs_settoken(creds.realm, uid, &creds)) { 360 log("AFS token (%s@%s) rejected for %s", 361 creds.pname, creds.realm, pw->pw_name); 362 memset(&creds, 0, sizeof(creds)); 363 return (0); 364 } 365 debug("AFS token accepted (%s@%s)", creds.pname, creds.realm); 366 memset(&creds, 0, sizeof(creds)); 367 368 return (1); 369 } 370 #endif /* AFS */ 371