static int krb4_adat(void *app_data, void *buf, size_t len) { KTEXT_ST tkt; AUTH_DAT auth_dat; char *p; int kerror; uint32_t cs; char msg[35]; /* size of encrypted block */ int tmp_len; struct krb4_data *d = app_data; char inst[INST_SZ]; struct sockaddr_in *his_addr_sin = (struct sockaddr_in *)his_addr; memcpy(tkt.dat, buf, len); tkt.length = len; k_getsockinst(0, inst, sizeof(inst)); kerror = krb_rd_req(&tkt, "ftp", inst, his_addr_sin->sin_addr.s_addr, &auth_dat, ""); if(kerror == RD_AP_UNDEC){ k_getsockinst(0, inst, sizeof(inst)); kerror = krb_rd_req(&tkt, "rcmd", inst, his_addr_sin->sin_addr.s_addr, &auth_dat, ""); } if(kerror){ reply(535, "Error reading request: %s.", krb_get_err_text(kerror)); return -1; } memcpy(d->key, auth_dat.session, sizeof(d->key)); des_set_key(&d->key, d->schedule); strlcpy(d->name, auth_dat.pname, sizeof(d->name)); strlcpy(d->instance, auth_dat.pinst, sizeof(d->instance)); strlcpy(d->realm, auth_dat.prealm, sizeof(d->instance)); cs = auth_dat.checksum + 1; { unsigned char tmp[4]; KRB_PUT_INT(cs, tmp, 4, sizeof(tmp)); tmp_len = krb_mk_safe(tmp, msg, 4, &d->key, (struct sockaddr_in *)LOCAL_ADDR, (struct sockaddr_in *)REMOTE_ADDR); } if(tmp_len < 0){ reply(535, "Error creating reply: %s.", strerror(errno)); return -1; } len = tmp_len; if(base64_encode(msg, len, &p) < 0) { reply(535, "Out of memory base64-encoding."); return -1; } reply(235, "ADAT=%s", p); sec_complete = 1; free(p); return 0; }
int Krb4Validate(RPC2_CountedBS * cIdent, RPC2_EncryptionKey hKey, RPC2_EncryptionKey sKey) { struct ktext *authenticator; struct auth_dat ticket; char *host = NULL; int rc = -1; if (cIdent->SeqLen != sizeof(struct ktext)) { fprintf(stderr, "Krb4Validate: cIdent too small to be authenticator"); return -1; } host = krb_canonicalize_host(NULL); if (!host) return -1; authenticator = (struct ktext *)cIdent->SeqBody; rc = krb_rd_req(authenticator, kerberos4service, host, 0, &ticket, ""); free(host); if (rc) { /* some kind of error */ fprintf(stderr, "Krb4Validate: %s (%d)\n", krb_err_txt[rc], rc); return -1; } /* Check whether the realm is correct */ if (strncmp(ticket.prealm, kerberos4realm, REALM_SZ)) { /* names differ */ fprintf(stderr, "Krb4Validate: incorrect realm in ticket\n"); /* do we have to clean up the ticket?? -JH */ return -1; } /* Copy kerberos name back into cIdent. There should be room as it first * got to us via cIdent anyway */ /* but let's make sure... */ assert(ANAME_SZ + INST_SZ + 1 <= cIdent->SeqLen); snprintf(cIdent->SeqBody, cIdent->SeqLen, "%s.%s", ticket.pname, ticket.pinst); cIdent->SeqLen = strlen(cIdent->SeqBody); if (cIdent->SeqLen && cIdent->SeqBody[cIdent->SeqLen-1] == '.') { /* user.@realm == user@realm */ cIdent->SeqLen--; cIdent->SeqBody[cIdent->SeqLen] = '\0'; } cIdent->SeqLen++; /* include trailing '\0' */ /* now prepare the keys */ /* hKey is the md5 hash of the kerberos session secret */ HashSecret(ticket.session, RPC2_KEYSIZE, hKey); /* sKey is a random sequence of bytes */ GenerateSecret(sKey); return 0; }
static Code_t ZCheckAuthentication4(ZNotice_t *notice, struct sockaddr_in *from) { int result; char srcprincipal[ANAME_SZ+INST_SZ+REALM_SZ+4]; KTEXT_ST authent; AUTH_DAT dat; ZChecksum_t checksum; char instance[INST_SZ+1]; if (!notice->z_auth) return ZAUTH_NO; /* Check for bogus authentication data length. */ if (notice->z_authent_len <= 0) return ZAUTH_FAILED; /* Read in the authentication data. */ if (ZReadAscii(notice->z_ascii_authent, strlen(notice->z_ascii_authent)+1, (unsigned char *)authent.dat, notice->z_authent_len) == ZERR_BADFIELD) { return ZAUTH_FAILED; } authent.length = notice->z_authent_len; strcpy(instance, SERVER_INSTANCE); /* We don't have the session key cached; do it the long way. */ result = krb_rd_req(&authent, SERVER_SERVICE, instance, from->sin_addr.s_addr, &dat, srvtab_file); if (result == RD_AP_OK) { ZSetSessionDES(&dat.session); sprintf(srcprincipal, "%s%s%s@%s", dat.pname, dat.pinst[0] ? "." : "", dat.pinst, dat.prealm); if (strcmp(srcprincipal, notice->z_sender)) return ZAUTH_FAILED; } else { return ZAUTH_FAILED; /* didn't decode correctly */ } /* Check the cryptographic checksum. */ checksum = compute_checksum(notice, dat.session); if (checksum != notice->z_checksum) return ZAUTH_FAILED; return ZAUTH_YES; }
/* * GetKerberosData * * get ticket from file descriptor and decode it. * Return KFAILURE if we barf on reading the ticket, else return * the value of rd_ap_req() applied to the ticket. */ int GetKerberosData(int fd, /* file descr. to read from */ struct in_addr haddr, /* address of foreign host on fd */ AUTH_DAT *kdata, /* kerberos data (returned) */ char *service, /* service principal desired */ char *srvtab) /* file to get keys from */ { char p[20]; KTEXT_ST ticket; /* will get Kerberos ticket from client */ unsigned int i; char instance[INST_SZ]; /* * Get the Kerberos ticket. The first few characters, terminated * by a blank, should give us a length; then get than many chars * which will be the ticket proper. */ for (i=0; i<20; i++) { if (read(fd, &p[i], 1) != 1) { syslog(LOG_WARNING,"bad read tkt len"); return(KFAILURE); } if (p[i] == ' ') { p[i] = '\0'; break; } } ticket.length = atoi(p); if ((i==20) || (ticket.length<=0) || (ticket.length>MAX_KTXT_LEN)) { syslog(LOG_WARNING,"bad tkt len %d",ticket.length); return(KFAILURE); } for (i=0; i<ticket.length; i++) { if (read(fd, &ticket.dat[i], 1) != 1) { syslog(LOG_WARNING,"bad tkt read"); return(KFAILURE); } } /* * now have the ticket. use it to get the authenticated * data from Kerberos. */ (void) strcpy(instance,"*"); /* let Kerberos fill it in */ return(krb_rd_req(&ticket, service, instance, haddr.s_addr, kdata, srvtab ? srvtab : "")); }
static int decrypt_tkt(char *user, char *instance, char *realm, char *arg, int (*key_proc)(char *, char *, char *, char *, C_Block), KTEXT *cipp) { MSG_DAT msg_data; /* Message data containing decrypted data */ KTEXT_ST auth; /* Authenticator */ AUTH_DAT auth_dat; /* Authentication data */ KTEXT cip = *cipp; MSG_DAT scip; int status = 0; des_cblock key; des_key_schedule sched; char phost[MAXHOSTNAMELEN + 1]; struct sockaddr_in caddr; /* client internet address */ struct sockaddr_in saddr; /* server internet address */ rkinitd_intkt_info *rii = (rkinitd_intkt_info *)arg; u_char enc_data[MAX_KTXT_LEN]; SBCLEAR(auth); SBCLEAR(auth_dat); SBCLEAR(scip); BCLEAR(enc_data); scip.app_data = enc_data; /* * Exchange with the client our response from the KDC (ticket encrypted * in user's private key) for the same ticket encrypted in our * (not yet known) session key. */ rpc_exchange_tkt(cip, &scip); /* * Get the authenticator */ SBCLEAR(auth); rpc_getauth(&auth, &caddr, &saddr); /* * Decode authenticator and extract session key. The first zero * means we don't care what host this comes from. This needs to * be done with euid of root so that /etc/srvtab can be read. */ BCLEAR(phost); this_phost(phost, sizeof(phost)); /* * This function has to use longjmp to return to the caller * because the kerberos library routine that calls it doesn't * pay attention to the return value it gives. That means that * if any of these routines failed, the error returned to the client * would be "password incorrect". */ status = krb_rd_req(&auth, KEY, phost, caddr.sin_addr.s_addr, &auth_dat, ""); if (status) { sprintf(errbuf, "krb_rd_req: %s", krb_err_txt[status]); rkinit_errmsg(errbuf); longjmp(rii->env, status); } memcpy(key, auth_dat.session, sizeof(key)); if (des_key_sched(key, sched)) { sprintf(errbuf, "Error in des_key_sched"); rkinit_errmsg(errbuf); longjmp(rii->env, RKINIT_DES); } /* Decrypt the data. */ if ((status = krb_rd_priv((u_char *)scip.app_data, scip.app_length, sched, key, &caddr, &saddr, &msg_data)) == KSUCCESS) { cip->length = msg_data.app_length; memcpy(cip->dat, msg_data.app_data, msg_data.app_length); cip->dat[cip->length] = 0; } else { sprintf(errbuf, "krb_rd_priv: %s", krb_err_txt[status]); rkinit_errmsg(errbuf); longjmp(rii->env, status); } if (setuid(user_id) < 0) { sprintf(errbuf, "Failure setting uid to %lu: %s\n", (unsigned long)user_id, strerror(errno)); rkinit_errmsg(errbuf); longjmp(rii->env, RKINIT_DAEMON); } return(KSUCCESS); }
char * /* R: allocated response string */ auth_krb4 ( /* PARAMETERS */ const char *login, /* I: plaintext authenticator */ const char *password, /* I: plaintext password */ const char *service, const char *realm_in /* END PARAMETERS */ ) { /* VARIABLES */ char aname[ANAME_SZ]; /* Kerberos principal */ const char *realm; /* Kerberos realm to authenticate in */ int rc; /* return code */ char tf_name[TF_NAME_LEN]; /* Ticket file name */ char *instance, *user_specified; KTEXT_ST ticket; AUTH_DAT kdata; /* END VARIABLES */ /* * Make sure we have a password. If this is NULL the call * to krb_get_pw_in_tkt below would try to prompt for * one interactively. */ if (password == NULL) { syslog(LOG_ERR, "auth_krb4: NULL password?"); return strdup("NO saslauthd internal error"); } if (krbtf_name(tf_name, sizeof(tf_name)) != 0) { syslog(LOG_ERR, "auth_krb4: could not generate ticket file name"); return strdup("NO saslauthd internal error"); } krb_set_tkt_string(tf_name); strncpy(aname, login, ANAME_SZ-1); aname[ANAME_SZ-1] = '\0'; instance = ""; if (config) { char keyname[1024]; snprintf(keyname, sizeof(keyname), "krb4_%s_instance", service); instance = cfile_getstring(config, keyname, ""); } user_specified = strchr(aname, '.'); if (user_specified) { if (instance && instance[0]) { /* sysadmin specified a (mandatory) instance */ if (strcmp(user_specified + 1, instance)) { return strdup("NO saslauthd principal name error"); } /* nuke instance from "aname"-- matches what's already in "instance" */ *user_specified = '\0'; } else { /* sysadmin has no preference, so we shift * instance name from "aname" to "instance" */ *user_specified = '\0'; instance = user_specified + 1; } } if(realm_in && *realm_in != '\0') { realm = realm_in; } else { realm = default_realm; } rc = krb_get_pw_in_tkt(aname, instance, realm, KRB_TICKET_GRANTING_TICKET, realm, 1, password); if (rc == INTK_BADPW || rc == KDC_PR_UNKNOWN) { return strdup("NO"); } else if (rc != KSUCCESS) { syslog(LOG_ERR, "ERROR: auth_krb4: krb_get_pw_in_tkt: %s", krb_get_err_text(rc)); return strdup("NO saslauthd internal error"); } /* if the TGT wasn't spoofed, it should entitle us to an rcmd ticket... */ rc = krb_mk_req(&ticket, verify_principal, myhostname, default_realm, 0); if (rc != KSUCCESS) { syslog(LOG_ERR, "ERROR: auth_krb4: krb_get_pw_in_tkt: %s", krb_get_err_text(rc)); dest_tkt(); return strdup("NO saslauthd internal error"); } /* .. and that ticket should match our secret host key */ rc = krb_rd_req(&ticket, verify_principal, myhostname, 0, &kdata, srvtabname); if (rc != RD_AP_OK) { syslog(LOG_ERR, "ERROR: auth_krb4: krb_rd_req:%s", krb_get_err_text(rc)); dest_tkt(); return strdup("NO saslauthd internal error"); } dest_tkt(); return strdup("OK"); }
void kerberos4_is(Authenticator *ap, unsigned char *data, int cnt) { struct sockaddr_in addr; char realm[REALM_SZ]; char instance[INST_SZ]; int r; int addr_len; if (cnt-- < 1) return; switch (*data++) { case KRB_AUTH: if (krb_get_lrealm(realm, 1) != KSUCCESS) { Data(ap, KRB_REJECT, (void *)"No local V4 Realm.", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("No local realm\r\n"); return; } memmove(auth.dat, data, auth.length = cnt); if (auth_debug_mode) { printf("Got %d bytes of authentication data\r\n", cnt); printf("CK: %d:", kerberos4_cksum(auth.dat, auth.length)); printd(auth.dat, auth.length); printf("\r\n"); } k_getsockinst(0, instance, sizeof(instance)); addr_len = sizeof(addr); if(getpeername(0, (struct sockaddr *)&addr, &addr_len) < 0) { if(auth_debug_mode) printf("getpeername failed\r\n"); Data(ap, KRB_REJECT, "getpeername failed", -1); auth_finished(ap, AUTH_REJECT); return; } r = krb_rd_req(&auth, KRB_SERVICE_NAME, instance, addr.sin_addr.s_addr, &adat, ""); if (r) { if (auth_debug_mode) printf("Kerberos failed him as %s\r\n", name); Data(ap, KRB_REJECT, (void *)krb_get_err_text(r), -1); auth_finished(ap, AUTH_REJECT); return; } /* save the session key */ memmove(session_key, adat.session, sizeof(adat.session)); krb_kntoln(&adat, name); if (UserNameRequested && !kuserok(&adat, UserNameRequested)){ char ts[MAXPATHLEN]; struct passwd *pw = getpwnam(UserNameRequested); if(pw){ snprintf(ts, sizeof(ts), "%s%u", TKT_ROOT, (unsigned)pw->pw_uid); setenv("KRBTKFILE", ts, 1); } Data(ap, KRB_ACCEPT, NULL, 0); } else { char *msg; asprintf (&msg, "user `%s' is not authorized to " "login as `%s'", krb_unparse_name_long(adat.pname, adat.pinst, adat.prealm), UserNameRequested ? UserNameRequested : "<nobody>"); if (msg == NULL) Data(ap, KRB_REJECT, NULL, 0); else { Data(ap, KRB_REJECT, (void *)msg, -1); free(msg); } } auth_finished(ap, AUTH_USER); break; case KRB_CHALLENGE: #ifndef ENCRYPTION Data(ap, KRB_RESPONSE, NULL, 0); #else if(!VALIDKEY(session_key)){ Data(ap, KRB_RESPONSE, NULL, 0); break; } des_key_sched(&session_key, sched); { des_cblock d_block; int i; Session_Key skey; memmove(d_block, data, sizeof(d_block)); /* make a session key for encryption */ des_ecb_encrypt(&d_block, &session_key, sched, 1); skey.type=SK_DES; skey.length=8; skey.data=session_key; encrypt_session_key(&skey, 1); /* decrypt challenge, add one and encrypt it */ des_ecb_encrypt(&d_block, &challenge, sched, 0); for (i = 7; i >= 0; i--) if(++challenge[i] != 0) break; des_ecb_encrypt(&challenge, &challenge, sched, 1); Data(ap, KRB_RESPONSE, (void *)challenge, sizeof(challenge)); } #endif break; case KRB_FORWARD: { des_key_schedule ks; unsigned char netcred[sizeof(CREDENTIALS)]; CREDENTIALS cred; int ret; if(cnt > sizeof(cred)) abort(); des_set_key(&session_key, ks); des_pcbc_encrypt((void*)data, (void*)netcred, cnt, ks, &session_key, DES_DECRYPT); unpack_cred(netcred, cnt, &cred); { if(strcmp(cred.service, KRB_TICKET_GRANTING_TICKET) || strncmp(cred.instance, cred.realm, sizeof(cred.instance)) || cred.lifetime < 0 || cred.lifetime > 255 || cred.kvno < 0 || cred.kvno > 255 || cred.issue_date < 0 || cred.issue_date > time(0) + CLOCK_SKEW || strncmp(cred.pname, adat.pname, sizeof(cred.pname)) || strncmp(cred.pinst, adat.pinst, sizeof(cred.pname))){ Data(ap, KRB_FORWARD_REJECT, "Bad credentials", -1); }else{ if((ret = tf_setup(&cred, cred.pname, cred.pinst)) == KSUCCESS){ struct passwd *pw = getpwnam(UserNameRequested); if (pw) chown(tkt_string(), pw->pw_uid, pw->pw_gid); Data(ap, KRB_FORWARD_ACCEPT, 0, 0); } else{ Data(ap, KRB_FORWARD_REJECT, krb_get_err_text(ret), -1); } } } memset(data, 0, cnt); memset(ks, 0, sizeof(ks)); memset(&cred, 0, sizeof(cred)); } break; default: if (auth_debug_mode) printf("Unknown Kerberos option %d\r\n", data[-1]); Data(ap, KRB_REJECT, 0, 0); break; } }
/* * try krb4 authentication, * return 1 on success, 0 on failure, -1 if krb4 is not available */ int auth_krb4_password(Authctxt *authctxt, const char *password) { AUTH_DAT adata; KTEXT_ST tkt; struct hostent *hp; struct passwd *pw; char localhost[MAXHOSTNAMELEN], phost[INST_SZ], realm[REALM_SZ]; u_int32_t faddr; int r; if ((pw = authctxt->pw) == NULL) return (0); /* * Try Kerberos password authentication only for non-root * users and only if Kerberos is installed. */ if (pw->pw_uid != 0 && krb_get_lrealm(realm, 1) == KSUCCESS) { /* Set up our ticket file. */ if (!krb4_init(authctxt)) { log("Couldn't initialize Kerberos ticket file for %s!", pw->pw_name); goto failure; } /* Try to get TGT using our password. */ r = krb_get_pw_in_tkt((char *) pw->pw_name, "", realm, "krbtgt", realm, DEFAULT_TKT_LIFE, (char *)password); if (r != INTK_OK) { debug("Kerberos v4 password authentication for %s " "failed: %s", pw->pw_name, krb_err_txt[r]); goto failure; } /* Successful authentication. */ chown(tkt_string(), pw->pw_uid, pw->pw_gid); /* * Now that we have a TGT, try to get a local * "rcmd" ticket to ensure that we are not talking * to a bogus Kerberos server. */ gethostname(localhost, sizeof(localhost)); strlcpy(phost, (char *)krb_get_phost(localhost), sizeof(phost)); r = krb_mk_req(&tkt, KRB4_SERVICE_NAME, phost, realm, 33); if (r == KSUCCESS) { if ((hp = gethostbyname(localhost)) == NULL) { log("Couldn't get local host address!"); goto failure; } memmove((void *)&faddr, (void *)hp->h_addr, sizeof(faddr)); /* Verify our "rcmd" ticket. */ r = krb_rd_req(&tkt, KRB4_SERVICE_NAME, phost, faddr, &adata, ""); if (r == RD_AP_UNDEC) { /* * Probably didn't have a srvtab on * localhost. Disallow login. */ log("Kerberos v4 TGT for %s unverifiable, " "no srvtab installed? krb_rd_req: %s", pw->pw_name, krb_err_txt[r]); goto failure; } else if (r != KSUCCESS) { log("Kerberos v4 %s ticket unverifiable: %s", KRB4_SERVICE_NAME, krb_err_txt[r]); goto failure; } } else if (r == KDC_PR_UNKNOWN) { /* * Disallow login if no rcmd service exists, and * log the error. */ log("Kerberos v4 TGT for %s unverifiable: %s; %s.%s " "not registered, or srvtab is wrong?", pw->pw_name, krb_err_txt[r], KRB4_SERVICE_NAME, phost); goto failure; } else { /* * TGT is bad, forget it. Possibly spoofed! */ debug("WARNING: Kerberos v4 TGT possibly spoofed " "for %s: %s", pw->pw_name, krb_err_txt[r]); goto failure; } /* Authentication succeeded. */ return (1); } else /* Logging in as root or no local Kerberos realm. */ debug("Unable to authenticate to Kerberos."); failure: krb4_cleanup_proc(authctxt); if (!options.kerberos_or_local_passwd) return (0); /* Fall back to ordinary passwd authentication. */ return (-1); }
int auth_krb4(Authctxt *authctxt, KTEXT auth, char **client, KTEXT reply) { AUTH_DAT adat = {0}; Key_schedule schedule; struct sockaddr_in local, foreign; char instance[INST_SZ]; socklen_t slen; u_int cksum; int r, s; s = packet_get_connection_in(); slen = sizeof(local); memset(&local, 0, sizeof(local)); if (getsockname(s, (struct sockaddr *) & local, &slen) < 0) debug("getsockname failed: %.100s", strerror(errno)); slen = sizeof(foreign); memset(&foreign, 0, sizeof(foreign)); if (getpeername(s, (struct sockaddr *) & foreign, &slen) < 0) { debug("getpeername failed: %.100s", strerror(errno)); fatal_cleanup(); } instance[0] = '*'; instance[1] = 0; /* Get the encrypted request, challenge, and session key. */ if ((r = krb_rd_req(auth, KRB4_SERVICE_NAME, instance, 0, &adat, ""))) { debug("Kerberos v4 krb_rd_req: %.100s", krb_err_txt[r]); return (0); } des_key_sched((des_cblock *) adat.session, schedule); *client = xmalloc(MAX_K_NAME_SZ); (void) snprintf(*client, MAX_K_NAME_SZ, "%s%s%s@%s", adat.pname, *adat.pinst ? "." : "", adat.pinst, adat.prealm); /* Check ~/.klogin authorization now. */ if (kuserok(&adat, authctxt->user) != KSUCCESS) { log("Kerberos v4 .klogin authorization failed for %s to " "account %s", *client, authctxt->user); xfree(*client); *client = NULL; return (0); } /* Increment the checksum, and return it encrypted with the session key. */ cksum = adat.checksum + 1; cksum = htonl(cksum); /* If we can't successfully encrypt the checksum, we send back an empty message, admitting our failure. */ if ((r = krb_mk_priv((u_char *) & cksum, reply->dat, sizeof(cksum) + 1, schedule, &adat.session, &local, &foreign)) < 0) { debug("Kerberos v4 mk_priv: (%d) %s", r, krb_err_txt[r]); reply->dat[0] = 0; reply->length = 0; } else reply->length = r; /* Clear session key. */ memset(&adat.session, 0, sizeof(&adat.session)); return (1); }
void kerberos_v4(struct sockaddr_in *client, KTEXT pkt) { static KTEXT_ST rpkt_st; KTEXT rpkt = &rpkt_st; static KTEXT_ST ciph_st; KTEXT ciph = &ciph_st; static KTEXT_ST tk_st; KTEXT tk = &tk_st; static KTEXT_ST auth_st; KTEXT auth = &auth_st; AUTH_DAT ad_st; AUTH_DAT *ad = &ad_st; static struct in_addr client_host; static int msg_byte_order; static int swap_bytes; static u_char k_flags; /* char *p_name, *instance; */ int lifetime = 0; int i; C_Block key; Key_schedule key_s; char *ptr; krb5_keyblock k5key; krb5_kvno kvno; krb5_deltat sk5life, ck5life; KRB4_32 v4endtime, v4req_end; k5key.contents = NULL; /* in case we have to free it */ ciph->length = 0; client_host = client->sin_addr; /* eval macros and correct the byte order and alignment as needed */ req_version = pkt_version(pkt); /* 1 byte, version */ req_msg_type = pkt_msg_type(pkt); /* 1 byte, Kerberos msg type */ /* set these to point to something safe */ req_name_ptr = req_inst_ptr = req_realm_ptr = ""; /* check if disabled, but we tell client */ if (kdc_v4 == KDC_V4_DISABLE) { lt = klog(L_KRB_PERR, "KRB will not handle v4 request from %s", inet_ntoa(client_host)); /* send an error reply */ req_name_ptr = req_inst_ptr = req_realm_ptr = ""; kerb_err_reply(client, pkt, KERB_ERR_PKT_VER, lt); return; } /* check packet version */ if (req_version != KRB_PROT_VERSION) { lt = klog(L_KRB_PERR, "KRB prot version mismatch: KRB =%d request = %d", KRB_PROT_VERSION, req_version, 0); /* send an error reply */ req_name_ptr = req_inst_ptr = req_realm_ptr = ""; kerb_err_reply(client, pkt, KERB_ERR_PKT_VER, lt); return; } msg_byte_order = req_msg_type & 1; swap_bytes = 0; if (msg_byte_order != HOST_BYTE_ORDER) { swap_bytes++; } klog(L_KRB_PINFO, "Prot version: %d, Byte order: %d, Message type: %d", (int) req_version, msg_byte_order, req_msg_type); switch (req_msg_type & ~1) { case AUTH_MSG_KDC_REQUEST: { int req_life; /* Requested liftime */ unsigned int request_backdate = 0; /*How far to backdate in seconds.*/ char *service; /* Service name */ char *instance; /* Service instance */ #ifdef notdef int kerno; /* Kerberos error number */ #endif n_auth_req++; tk->length = 0; k_flags = 0; /* various kerberos flags */ /* set up and correct for byte order and alignment */ req_name_ptr = (char *) pkt_a_name(pkt); str_length_check(req_name_ptr, ANAME_SZ); req_inst_ptr = (char *) pkt_a_inst(pkt); str_length_check(req_inst_ptr, INST_SZ); req_realm_ptr = (char *) pkt_a_realm(pkt); str_length_check(req_realm_ptr, REALM_SZ); memcpy(&req_time_ws, pkt_time_ws(pkt), sizeof(req_time_ws)); /* time has to be diddled */ if (swap_bytes) { swap_u_long(req_time_ws); } ptr = (char *) pkt_time_ws(pkt) + 4; req_life = (*ptr++) & 0xff; service = ptr; str_length_check(service, SNAME_SZ); instance = ptr + strlen(service) + 1; str_length_check(instance, INST_SZ); rpkt = &rpkt_st; klog(L_INI_REQ, "Initial ticket request Host: %s User: \"%s\" \"%s\"", inet_ntoa(client_host), req_name_ptr, req_inst_ptr, 0); if ((i = check_princ(req_name_ptr, req_inst_ptr, 0, &a_name_data, &k5key, 0, &ck5life))) { kerb_err_reply(client, pkt, i, "check_princ failed"); a_name_data.key_low = a_name_data.key_high = 0; krb5_free_keyblock_contents(kdc_context, &k5key); return; } /* don't use k5key for client */ krb5_free_keyblock_contents(kdc_context, &k5key); tk->length = 0; /* init */ if (strcmp(service, "krbtgt")) klog(L_NTGT_INTK, "INITIAL request from %s.%s for %s.%s", req_name_ptr, req_inst_ptr, service, instance, 0); /* this does all the checking */ if ((i = check_princ(service, instance, lifetime, &s_name_data, &k5key, 1, &sk5life))) { kerb_err_reply(client, pkt, i, "check_princ failed"); a_name_data.key_high = a_name_data.key_low = 0; s_name_data.key_high = s_name_data.key_low = 0; krb5_free_keyblock_contents(kdc_context, &k5key); return; } /* Bound requested lifetime with service and user */ v4req_end = krb_life_to_time(kerb_time.tv_sec, req_life); v4req_end = min(v4req_end, kerb_time.tv_sec + ck5life); v4req_end = min(v4req_end, kerb_time.tv_sec + sk5life); lifetime = krb_time_to_life(kerb_time.tv_sec, v4req_end); v4endtime = krb_life_to_time(kerb_time.tv_sec, lifetime); /* * Adjust issue time backwards if necessary, due to * roundup in krb_time_to_life(). */ if (v4endtime > v4req_end) request_backdate = v4endtime - v4req_end; #ifdef NOENCRYPTION memset(session_key, 0, sizeof(C_Block)); #else /* random session key */ des_new_random_key(session_key); #endif /* unseal server's key from master key */ memcpy( key, &s_name_data.key_low, 4); memcpy( ((krb5_ui_4 *) key) + 1, &s_name_data.key_high, 4); s_name_data.key_low = s_name_data.key_high = 0; kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); /* construct and seal the ticket */ /* We always issue des tickets; the 3des tickets are a broken hack*/ krb_create_ticket(tk, k_flags, a_name_data.name, a_name_data.instance, local_realm, client_host.s_addr, (char *) session_key, lifetime, kerb_time.tv_sec - request_backdate, s_name_data.name, s_name_data.instance, key); krb5_free_keyblock_contents(kdc_context, &k5key); memset(key, 0, sizeof(key)); memset(key_s, 0, sizeof(key_s)); /* * get the user's key, unseal it from the server's key, and * use it to seal the cipher */ /* a_name_data.key_low a_name_data.key_high */ memcpy( key, &a_name_data.key_low, 4); memcpy( ((krb5_ui_4 *) key) + 1, &a_name_data.key_high, 4); a_name_data.key_low= a_name_data.key_high = 0; /* unseal the a_name key from the master key */ kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); create_ciph(ciph, session_key, s_name_data.name, s_name_data.instance, local_realm, lifetime, s_name_data.key_version, tk, kerb_time.tv_sec, key); /* clear session key */ memset(session_key, 0, sizeof(session_key)); memset(key, 0, sizeof(key)); /* always send a reply packet */ rpkt = create_auth_reply(req_name_ptr, req_inst_ptr, req_realm_ptr, req_time_ws, 0, a_name_data.exp_date, a_name_data.key_version, ciph); krb4_sendto(f, (char *) rpkt->dat, rpkt->length, 0, (struct sockaddr *) client, sizeof (struct sockaddr_in)); memset(&a_name_data, 0, sizeof(a_name_data)); memset(&s_name_data, 0, sizeof(s_name_data)); break; } case AUTH_MSG_APPL_REQUEST: { krb5_ui_4 time_ws; /* Workstation time */ int req_life; /* Requested liftime */ char *service; /* Service name */ char *instance; /* Service instance */ int kerno = 0; /* Kerberos error number */ unsigned int request_backdate = 0; /*How far to backdate in seconds.*/ char tktrlm[REALM_SZ]; n_appl_req++; tk->length = 0; k_flags = 0; /* various kerberos flags */ auth->mbz = 0; /* pkt->mbz already zeroed */ auth->length = 4 + strlen((char *)pkt->dat + 3); if (auth->length + 1 > MAX_KTXT_LEN) { lt = klog(L_KRB_PERR, "APPL request with realm length too long from %s", inet_ntoa(client_host)); kerb_err_reply(client, pkt, RD_AP_INCON, "realm length too long"); return; } auth->length += (int) *(pkt->dat + auth->length) + (int) *(pkt->dat + auth->length + 1) + 2; if (auth->length > MAX_KTXT_LEN) { lt = klog(L_KRB_PERR, "APPL request with funky tkt or req_id length from %s", inet_ntoa(client_host)); kerb_err_reply(client, pkt, RD_AP_INCON, "funky tkt or req_id length"); return; } memcpy(auth->dat, pkt->dat, auth->length); strncpy(tktrlm, (char *)auth->dat + 3, REALM_SZ); tktrlm[REALM_SZ-1] = '\0'; kvno = (krb5_kvno)auth->dat[2]; if ((!allow_v4_crossrealm)&&strcmp(tktrlm, local_realm) != 0) { lt = klog(L_ERR_UNK, "Cross realm ticket from %s denied by policy,", tktrlm); kerb_err_reply(client, pkt, KERB_ERR_PRINCIPAL_UNKNOWN, lt); return; } if (set_tgtkey(tktrlm, kvno, 0)) { lt = klog(L_ERR_UNK, "FAILED set_tgtkey realm %s, kvno %d. Host: %s ", tktrlm, kvno, inet_ntoa(client_host)); /* no better error code */ kerb_err_reply(client, pkt, KERB_ERR_PRINCIPAL_UNKNOWN, lt); return; } kerno = krb_rd_req(auth, "krbtgt", tktrlm, client_host.s_addr, ad, 0); if (kerno) { if (set_tgtkey(tktrlm, kvno, 1)) { lt = klog(L_ERR_UNK, "FAILED 3des set_tgtkey realm %s, kvno %d. Host: %s ", tktrlm, kvno, inet_ntoa(client_host)); /* no better error code */ kerb_err_reply(client, pkt, KERB_ERR_PRINCIPAL_UNKNOWN, lt); return; } kerno = krb_rd_req(auth, "krbtgt", tktrlm, client_host.s_addr, ad, 0); } if (kerno) { klog(L_ERR_UNK, "FAILED krb_rd_req from %s: %s", inet_ntoa(client_host), krb_get_err_text(kerno)); req_name_ptr = req_inst_ptr = req_realm_ptr = ""; kerb_err_reply(client, pkt, kerno, "krb_rd_req failed"); return; } ptr = (char *) pkt->dat + auth->length; memcpy(&time_ws, ptr, 4); ptr += 4; req_life = (*ptr++) & 0xff; service = ptr; str_length_check(service, SNAME_SZ); instance = ptr + strlen(service) + 1; str_length_check(instance, INST_SZ); klog(L_APPL_REQ, "APPL Request %s.%s@%s on %s for %s.%s", ad->pname, ad->pinst, ad->prealm, inet_ntoa(client_host), service, instance, 0); req_name_ptr = ad->pname; req_inst_ptr = ad->pinst; req_realm_ptr = ad->prealm; if (strcmp(ad->prealm, tktrlm)) { kerb_err_reply(client, pkt, KERB_ERR_PRINCIPAL_UNKNOWN, "Can't hop realms"); return; } if (!strcmp(service, "changepw")) { kerb_err_reply(client, pkt, KERB_ERR_PRINCIPAL_UNKNOWN, "Can't authorize password changed based on TGT"); return; } kerno = check_princ(service, instance, req_life, &s_name_data, &k5key, 1, &sk5life); if (kerno) { kerb_err_reply(client, pkt, kerno, "check_princ failed"); s_name_data.key_high = s_name_data.key_low = 0; krb5_free_keyblock_contents(kdc_context, &k5key); return; } /* Bound requested lifetime with service and user */ v4endtime = krb_life_to_time((KRB4_32)ad->time_sec, ad->life); v4req_end = krb_life_to_time(kerb_time.tv_sec, req_life); v4req_end = min(v4endtime, v4req_end); v4req_end = min(v4req_end, kerb_time.tv_sec + sk5life); lifetime = krb_time_to_life(kerb_time.tv_sec, v4req_end); v4endtime = krb_life_to_time(kerb_time.tv_sec, lifetime); /* * Adjust issue time backwards if necessary, due to * roundup in krb_time_to_life(). */ if (v4endtime > v4req_end) request_backdate = v4endtime - v4req_end; /* unseal server's key from master key */ memcpy(key, &s_name_data.key_low, 4); memcpy(((krb5_ui_4 *) key) + 1, &s_name_data.key_high, 4); s_name_data.key_low = s_name_data.key_high = 0; kdb_encrypt_key(key, key, master_key, master_key_schedule, DECRYPT); /* construct and seal the ticket */ #ifdef NOENCRYPTION memset(session_key, 0, sizeof(C_Block)); #else /* random session key */ des_new_random_key(session_key); #endif /* ALways issue des tickets*/ krb_create_ticket(tk, k_flags, ad->pname, ad->pinst, ad->prealm, client_host.s_addr, (char *) session_key, lifetime, kerb_time.tv_sec - request_backdate, s_name_data.name, s_name_data.instance, key); krb5_free_keyblock_contents(kdc_context, &k5key); memset(key, 0, sizeof(key)); memset(key_s, 0, sizeof(key_s)); create_ciph(ciph, session_key, service, instance, local_realm, lifetime, s_name_data.key_version, tk, kerb_time.tv_sec, ad->session); /* clear session key */ memset(session_key, 0, sizeof(session_key)); memset(ad->session, 0, sizeof(ad->session)); rpkt = create_auth_reply(ad->pname, ad->pinst, ad->prealm, time_ws, 0, 0, 0, ciph); krb4_sendto(f, (char *) rpkt->dat, rpkt->length, 0, (struct sockaddr *) client, sizeof (struct sockaddr_in)); memset(&s_name_data, 0, sizeof(s_name_data)); break; } #ifdef notdef_DIE case AUTH_MSG_DIE: { lt = klog(L_DEATH_REQ, "Host: %s User: \"%s\" \"%s\" Kerberos killed", inet_ntoa(client_host), req_name_ptr, req_inst_ptr, 0); exit(0); } #endif /* notdef_DIE */ default: { lt = klog(L_KRB_PERR, "Unknown message type: %d from %s port %u", req_msg_type, inet_ntoa(client_host), ntohs(client->sin_port)); break; } } }