int do_krb5_login (int infd, struct auth_data *ap, const char **err_msg) { krb5_auth_context auth_ctx = NULL; krb5_error_code status; krb5_data inbuf; krb5_data version; krb5_authenticator *authenticator; krb5_rcache rcache; krb5_keyblock *key; krb5_ticket *ticket; struct sockaddr_in laddr; int len; struct passwd *pwd; char *name; if (status = krb5_init_context (&ap->context)) { syslog (LOG_ERR, "Error initializing krb5: %s", error_message (status)); return status; } if ((status = krb5_auth_con_init (ap->context, &auth_ctx)) || (status = krb5_auth_con_genaddrs (ap->context, auth_ctx, infd, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)) || (status = krb5_auth_con_getrcache (ap->context, auth_ctx, &rcache))) return status; if (!rcache) { krb5_principal server; status = krb5_sname_to_principal (ap->context, 0, 0, KRB5_NT_SRV_HST, &server); if (status) return status; status = krb5_get_server_rcache (ap->context, krb5_princ_component (ap->context, server, 0), &rcache); krb5_free_principal (ap->context, server); if (status) return status; status = krb5_auth_con_setrcache (ap->context, auth_ctx, rcache); if (status) return status; } len = sizeof (laddr); if (getsockname (infd, (struct sockaddr *) &laddr, &len)) return errno; status = krb5_recvauth (ap->context, &auth_ctx, &infd, NULL, 0, 0, ap->keytab, &ticket); if (status) return status; if ((status = krb5_auth_con_getauthenticator (ap->context, auth_ctx, &authenticator))) return status; getstr (infd, &ap->lusername, NULL); getstr (infd, &ap->term, "TERM="); pwd = getpwnam (ap->lusername); if (pwd == NULL) { *err_msg = "getpwnam failed"; syslog (LOG_ERR, "getpwnam failed: %m"); return 1; } getstr (infd, &ap->rusername, NULL); if ((status = krb5_copy_principal (ap->context, ticket->enc_part2->client, &ap->client))) return status; /*OK:: */ if (ap->client && !krb5_kuserok (ap->context, ap->client, ap->lusername)) return 1; krb5_unparse_name (ap->context, ap->client, &name); syslog (LOG_INFO | LOG_AUTH, "%sKerberos V login from %s on %s\n", (pwd->pw_uid == 0) ? "ROOT " : "", name, ap->hostname); free (name); return 0; }
/* XXX - check for cleanup */ krb5_error_code setup_auth_context(krb5_context context, krb5_auth_context auth_context, struct sockaddr_in *localaddr, struct sockaddr_in *remoteaddr, char *uniq) { krb5_address laddr, raddr, *portlocal_addr; krb5_rcache rcache; krb5_data rcache_name; char *outaddr; krb5_error_code retval; #ifndef HEIMDAL /* Setting ports isn't compatible with Heimdal, if this code is enabled, it's not possible to have an interoperable setup */ #if 0 laddr.addrtype = ADDRTYPE_IPPORT; laddr.length = sizeof(localaddr->sin_port); laddr.contents = (krb5_octet *)&(localaddr->sin_port); raddr.addrtype = ADDRTYPE_IPPORT; raddr.length = sizeof(remoteaddr->sin_port); raddr.contents = (krb5_octet *)&(remoteaddr->sin_port); if (retval = krb5_auth_con_setports(context, auth_context, &laddr, &raddr)) { sprintf(auth_con_error, "%s while setting auth_con ports\n", error_message(retval)); return retval; } #endif #endif #ifdef HEIMDAL laddr.addr_type = KRB5_ADDRESS_INET; laddr.address.length = sizeof(localaddr->sin_addr); laddr.address.data = (void *)&(localaddr->sin_addr); raddr.addr_type = KRB5_ADDRESS_INET; raddr.address.length = sizeof(remoteaddr->sin_addr); raddr.address.data = (void *)&(remoteaddr->sin_addr); #else laddr.addrtype = ADDRTYPE_INET; laddr.length = sizeof(localaddr->sin_addr); laddr.contents = (krb5_octet *)&(localaddr->sin_addr); raddr.addrtype = ADDRTYPE_INET; raddr.length = sizeof(remoteaddr->sin_addr); raddr.contents = (krb5_octet *)&(remoteaddr->sin_addr); #endif if (retval = krb5_auth_con_setaddrs(context, auth_context, &laddr, &raddr)) { sprintf(auth_con_error, "%s while setting auth_con addresses\n", error_message(retval)); return retval; } #ifdef HEIMDAL #else /* Set up replay cache */ if ((retval = krb5_gen_portaddr(context, &laddr, (krb5_pointer) &(localaddr->sin_port), &portlocal_addr))) { sprintf(auth_con_error, "%s while generating port address", error_message(retval)); return retval; } if ((retval = krb5_gen_replay_name(context, portlocal_addr, uniq, &outaddr))) { sprintf(auth_con_error, "%s while generating replay cache name", error_message(retval)); return retval; } rcache_name.length = strlen(outaddr); rcache_name.data = outaddr; if ((retval = krb5_get_server_rcache(context, &rcache_name, &rcache))) { sprintf(auth_con_error, "%s while getting server rcache", error_message(retval)); return retval; } if (retval = krb5_auth_con_setrcache(context, auth_context, rcache)) { sprintf(auth_con_error, "%s setting rcache", error_message(retval)); return retval; } #endif return retval; }
int do_krb5_comm(krb5_context context, krb5_keytab keytab, krb5_principal server, char *cmddir) { struct sockaddr_in c_saddr, s_saddr; socklen_t namelen; int sock = 0; int len; char buff[BUFFSIZE]; char *cname = NULL; krb5_error_code retval; krb5_data kdata, message; krb5_auth_context auth_context = NULL; krb5_ticket *ticket; krb5_address ckaddr, skaddr; krb5_rcache rcache; krb5_data rcache_name; long srand, rrand; int fd[2]; char rcname_piece[RC_PIECE_MAXLEN]; namelen = sizeof(c_saddr); if (getpeername(sock, (struct sockaddr *)&c_saddr, &namelen) < 0) { syslog(LOG_ERR, "getpeername: %m"); return 1; } namelen = sizeof(s_saddr); if (getsockname(sock, (struct sockaddr *)&s_saddr, &namelen) < 0) { syslog(LOG_ERR, "getsockname: %m"); return 1; } /* INIT MSG = random number */ srand = random(); /* Send it */ if (send(sock, &srand, sizeof(srand), 0) < 0) { syslog(LOG_ERR, "%m while sending init message"); return 1; } if (recv(sock, &rrand, sizeof(rrand), 0) < 0) { syslog(LOG_ERR, "%m while receiving init reply"); return 1; } /* Reply should contain the same message (number) */ if (srand != rrand) { syslog(LOG_ERR, "Bad init reply"); return 1; } /* Do authentication */ if (retval = krb5_recvauth(context, &auth_context, (krb5_pointer)&sock, AFSADM_VERSION, server, 0, keytab, &ticket)) { syslog(LOG_ERR, "recvauth failed: %s", error_message(retval)); exit(1); } /* Get client name */ if (retval = krb5_unparse_name(context, ticket->enc_part2->client, &cname)) { syslog(LOG_ERR, "unparse failed: %s", error_message(retval)); return 1; } if (ticket) krb5_free_ticket(context, ticket); if (debug) syslog(LOG_DEBUG, "Principal %s", cname); /*******************************************************************/ ckaddr.addrtype = ADDRTYPE_IPPORT; ckaddr.length = sizeof(c_saddr.sin_port); ckaddr.contents = (krb5_octet *)&c_saddr.sin_port; skaddr.addrtype = ADDRTYPE_IPPORT; skaddr.length = sizeof(s_saddr.sin_port); skaddr.contents = (krb5_octet *)&s_saddr.sin_port; if ((retval = krb5_auth_con_setports(context, auth_context, &skaddr, &ckaddr))) { syslog(LOG_ERR, "%s while setting ports", error_message(retval)); return 1; } /* Set foreign_addr for rd_priv() */ ckaddr.addrtype = ADDRTYPE_INET; ckaddr.length = sizeof(c_saddr.sin_addr); ckaddr.contents = (krb5_octet *)&c_saddr.sin_addr; /* Set local_addr */ skaddr.addrtype = ADDRTYPE_INET; skaddr.length = sizeof(s_saddr.sin_addr); skaddr.contents = (krb5_octet *)&s_saddr.sin_addr; if ((retval = krb5_auth_con_setaddrs(context, auth_context, &skaddr, &ckaddr))) { syslog(LOG_ERR, "%s while setting addrs", error_message(retval)); return 1; } /* Receive a request */ if ((len = recv(sock, (char *)buff, sizeof(buff), 0)) < 0) { syslog(LOG_ERR, "%m while receiving datagram"); return 1; } kdata.length = len; kdata.data = buff; if (debug) syslog(LOG_DEBUG, "Received %d bytes", len); /* Decrypt it */ if ((retval = krb5_rd_priv(context, auth_context, &kdata, &message, NULL))) { syslog(LOG_ERR, "%s while verifying PRIV message", error_message(retval)); return 1; } if (message.length > 0) { #ifdef __osf__ sprintf(rcname_piece, "afsadmd_%d", getpid()); #else snprintf(rcname_piece, RC_PIECE_MAXLEN, "afsadmd_%d", getpid()); #endif rcache_name.data = rcname_piece; rcache_name.length = strlen(rcache_name.data); if ((retval = krb5_get_server_rcache(context, &rcache_name, &rcache))) { syslog(LOG_ERR, "%s while getting server rcache", error_message(retval)); return 1; } /* set auth_context rcache */ if (retval = krb5_auth_con_setrcache(context, auth_context, rcache)) { syslog(LOG_ERR, "%s while setting rcache", error_message(retval)); return 1; } /********************************************************************* * Call the desired command, read stdout/stderr, send it *********************************************************************/ /* create fork */ if (pipe(fd) == -1) printf("Failed create fork with pipe().\n"); if (fork() == 0) { close(fd[0]); close(1); close(2); dup2(fd[1], 1); dup2(fd[1], 2); /* Call required command */ do_command(context, keytab, server, cname, message.data, cmddir ); krb5_xfree(message.data); exit(0); } else { /* Read stdout/stderr from pipe, store it to the buffer, encrypt it a send to the client */ krb5_data message, kdata; char buff[PIPEBUFF]; int n = 0; int len = 0; int sent = 0; int counter = 0; int end = 0; short netlen; time_t starttime, oldtime, newtime; FILE *pipedes; close(fd[1]); pipedes = fdopen(fd[0], "r"); starttime = oldtime = time(NULL); for (n = 0; end == 0; ) { /* Read line from pipe */ if (fgets(buff + n, PIPEBUFF - n, pipedes) == NULL) end++; else n = strlen(buff); /* Get time */ newtime = time(NULL); /* Send buffer when * a) buffer is full * b) buffer contains data and * 1) end-of-file encountered (end flag) * 2) buffer sent before 1s */ if ((n == PIPEBUFF) || (((newtime > oldtime) || end ) && (n != 0))) { /* Prepare data for sending */ message.data = buff; message.length = n; kdata.data = NULL; /* Make the encrypted message */ if ((retval = krb5_mk_priv(context, auth_context, &message, &kdata, NULL))) { syslog(LOG_ERR, "%s while making KRB_PRIV message", error_message(retval)); return 1; } /* Convert byte order */ netlen = htons((short)kdata.length); /* Send len of encrypted data */ if ((len = send(sock, (char *)&netlen, sizeof(netlen), 0)) != sizeof(netlen)) { krb5_xfree(kdata.data); syslog(LOG_ERR, "%m while sending len of PRIV message"); return 1; } /* Send it */ if ((len = send(sock, (char *)kdata.data, kdata.length, 0)) != kdata.length) { syslog(LOG_ERR, "%m while sending PRIV message"); krb5_xfree(kdata.data); return 1; } /* Statistics */ sent += len; counter++; /* Timestanmp */ oldtime = newtime; n = 0; krb5_xfree(kdata.data); } } newtime = time(NULL); if (debug) syslog(LOG_DEBUG, "Sent %d bytes in %ds [%d fragment(s)]", sent, (int)(newtime - starttime), counter); } } //FIXME: There is no way to close or destroy rcache declared in krb5 headers //krb5_rc_destroy(context, rcache); /* set auth_context rcache */ if (retval = krb5_auth_con_setrcache(context, auth_context, rcache)) { syslog(LOG_ERR, "%s while setting rcache to NULL", error_message(retval)); return 1; } free(cname); krb5_auth_con_free(context, auth_context); return 0; }
NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, const char *realm, time_t time_offset, const DATA_BLOB *ticket, char **principal, struct PAC_DATA **pac_data, DATA_BLOB *ap_rep, DATA_BLOB *session_key, bool use_replay_cache) { NTSTATUS sret = NT_STATUS_LOGON_FAILURE; NTSTATUS pac_ret; DATA_BLOB auth_data; krb5_context context = NULL; krb5_auth_context auth_context = NULL; krb5_data packet; krb5_ticket *tkt = NULL; krb5_rcache rcache = NULL; krb5_keyblock *keyblock = NULL; time_t authtime; krb5_error_code ret = 0; int flags = 0; krb5_principal host_princ = NULL; krb5_const_principal client_principal = NULL; char *host_princ_s = NULL; bool auth_ok = False; bool got_auth_data = False; struct named_mutex *mutex = NULL; ZERO_STRUCT(packet); ZERO_STRUCT(auth_data); *principal = NULL; *pac_data = NULL; *ap_rep = data_blob_null; *session_key = data_blob_null; initialize_krb5_error_table(); ret = krb5_init_context(&context); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_init_context failed (%s)\n", error_message(ret))); return NT_STATUS_LOGON_FAILURE; } if (time_offset != 0) { krb5_set_real_time(context, time(NULL) + time_offset, 0); } ret = krb5_set_default_realm(context, realm); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_set_default_realm failed (%s)\n", error_message(ret))); goto out; } /* This whole process is far more complex than I would like. We have to go through all this to allow us to store the secret internally, instead of using /etc/krb5.keytab */ ret = krb5_auth_con_init(context, &auth_context); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_auth_con_init failed (%s)\n", error_message(ret))); goto out; } krb5_auth_con_getflags( context, auth_context, &flags ); if ( !use_replay_cache ) { /* Disable default use of a replay cache */ flags &= ~KRB5_AUTH_CONTEXT_DO_TIME; krb5_auth_con_setflags( context, auth_context, flags ); } if (asprintf(&host_princ_s, "%s$", global_myname()) == -1) { goto out; } strlower_m(host_princ_s); ret = smb_krb5_parse_name(context, host_princ_s, &host_princ); if (ret) { DEBUG(1,("ads_verify_ticket: smb_krb5_parse_name(%s) failed (%s)\n", host_princ_s, error_message(ret))); goto out; } if ( use_replay_cache ) { /* Lock a mutex surrounding the replay as there is no locking in the MIT krb5 code surrounding the replay cache... */ mutex = grab_named_mutex(talloc_tos(), "replay cache mutex", 10); if (mutex == NULL) { DEBUG(1,("ads_verify_ticket: unable to protect " "replay cache with mutex.\n")); ret = KRB5_CC_IO; goto out; } /* JRA. We must set the rcache here. This will prevent replay attacks. */ ret = krb5_get_server_rcache(context, krb5_princ_component(context, host_princ, 0), &rcache); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache " "failed (%s)\n", error_message(ret))); goto out; } ret = krb5_auth_con_setrcache(context, auth_context, rcache); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache " "failed (%s)\n", error_message(ret))); goto out; } } /* Try secrets.tdb first and fallback to the krb5.keytab if necessary */ auth_ok = ads_secrets_verify_ticket(context, auth_context, host_princ, ticket, &tkt, &keyblock, &ret); if (!auth_ok && (ret == KRB5KRB_AP_ERR_TKT_NYV || ret == KRB5KRB_AP_ERR_TKT_EXPIRED || ret == KRB5KRB_AP_ERR_SKEW)) { goto auth_failed; } if (!auth_ok && lp_use_kerberos_keytab()) { auth_ok = ads_keytab_verify_ticket(context, auth_context, ticket, &tkt, &keyblock, &ret); } if ( use_replay_cache ) { TALLOC_FREE(mutex); #if 0 /* Heimdal leaks here, if we fix the leak, MIT crashes */ if (rcache) { krb5_rc_close(context, rcache); } #endif } auth_failed: if (!auth_ok) { DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n", error_message(ret))); /* Try map the error return in case it's something like * a clock skew error. */ sret = krb5_to_nt_status(ret); if (NT_STATUS_IS_OK(sret) || NT_STATUS_EQUAL(sret,NT_STATUS_UNSUCCESSFUL)) { sret = NT_STATUS_LOGON_FAILURE; } DEBUG(10,("ads_verify_ticket: returning error %s\n", nt_errstr(sret) )); goto out; } authtime = get_authtime_from_tkt(tkt); client_principal = get_principal_from_tkt(tkt); ret = krb5_mk_rep(context, auth_context, &packet); if (ret) { DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n", error_message(ret))); goto out; } *ap_rep = data_blob(packet.data, packet.length); if (packet.data) { kerberos_free_data_contents(context, &packet); ZERO_STRUCT(packet); } get_krb5_smb_session_key(context, auth_context, session_key, True); dump_data_pw("SMB session key (from ticket)\n", session_key->data, session_key->length); #if 0 file_save("/tmp/ticket.dat", ticket->data, ticket->length); #endif /* continue when no PAC is retrieved or we couldn't decode the PAC (like accounts that have the UF_NO_AUTH_DATA_REQUIRED flag set, or Kerberos tickets encrypted using a DES key) - Guenther */ got_auth_data = get_auth_data_from_tkt(mem_ctx, &auth_data, tkt); if (!got_auth_data) { DEBUG(3,("ads_verify_ticket: did not retrieve auth data. continuing without PAC\n")); } if (got_auth_data) { pac_ret = decode_pac_data(mem_ctx, &auth_data, context, keyblock, client_principal, authtime, pac_data); if (!NT_STATUS_IS_OK(pac_ret)) { DEBUG(3,("ads_verify_ticket: failed to decode PAC_DATA: %s\n", nt_errstr(pac_ret))); *pac_data = NULL; } data_blob_free(&auth_data); } #if 0 #if defined(HAVE_KRB5_TKT_ENC_PART2) /* MIT */ if (tkt->enc_part2) { file_save("/tmp/authdata.dat", tkt->enc_part2->authorization_data[0]->contents, tkt->enc_part2->authorization_data[0]->length); } #else /* Heimdal */ if (tkt->ticket.authorization_data) { file_save("/tmp/authdata.dat", tkt->ticket.authorization_data->val->ad_data.data, tkt->ticket.authorization_data->val->ad_data.length); } #endif #endif if ((ret = smb_krb5_unparse_name(context, client_principal, principal))) { DEBUG(3,("ads_verify_ticket: smb_krb5_unparse_name failed (%s)\n", error_message(ret))); sret = NT_STATUS_LOGON_FAILURE; goto out; } sret = NT_STATUS_OK; out: TALLOC_FREE(mutex); if (!NT_STATUS_IS_OK(sret)) { data_blob_free(&auth_data); } if (!NT_STATUS_IS_OK(sret)) { data_blob_free(ap_rep); } if (host_princ) { krb5_free_principal(context, host_princ); } if (keyblock) { krb5_free_keyblock(context, keyblock); } if (tkt != NULL) { krb5_free_ticket(context, tkt); } SAFE_FREE(host_princ_s); if (auth_context) { krb5_auth_con_free(context, auth_context); } if (context) { krb5_free_context(context); } return sret; }
static krb5_error_code recvauth(int f, krb5_context krb_context, unsigned int *valid_checksum, krb5_ticket **ticket, int *auth_type, krb5_principal *client, int encr_flag, krb5_keytab keytab) { krb5_error_code status = 0; krb5_auth_context auth_context = NULL; krb5_rcache rcache; krb5_authenticator *authenticator; krb5_data inbuf; krb5_data auth_version; *valid_checksum = 0; if ((status = krb5_auth_con_init(krb_context, &auth_context))) return (status); /* Only need remote address for rd_cred() to verify client */ if ((status = krb5_auth_con_genaddrs(krb_context, auth_context, f, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR))) return (status); status = krb5_auth_con_getrcache(krb_context, auth_context, &rcache); if (status) return (status); if (!rcache) { krb5_principal server; status = krb5_sname_to_principal(krb_context, 0, 0, KRB5_NT_SRV_HST, &server); if (status) return (status); status = krb5_get_server_rcache(krb_context, krb5_princ_component(krb_context, server, 0), &rcache); krb5_free_principal(krb_context, server); if (status) return (status); status = krb5_auth_con_setrcache(krb_context, auth_context, rcache); if (status) return (status); } if ((status = krb5_compat_recvauth(krb_context, &auth_context, &f, NULL, /* Specify daemon principal */ 0, /* no flags */ keytab, /* NULL to use v5srvtab */ ticket, /* return ticket */ auth_type, /* authentication system */ &auth_version))) { if (*auth_type == KRB5_RECVAUTH_V5) { /* * clean up before exiting */ getstr(f, rusername, sizeof (rusername), "remuser"); getstr(f, lusername, sizeof (lusername), "locuser"); getstr(f, term, sizeof (term), "Terminal type"); } return (status); } getstr(f, lusername, sizeof (lusername), "locuser"); getstr(f, term, sizeof (term), "Terminal type"); kcmd_protocol = KCMD_UNKNOWN_PROTOCOL; if (auth_version.length != 9 || auth_version.data == NULL) { syslog(LOG_ERR, "Bad application protocol version length in " "KRB5 exchange, exiting"); fatal(f, "Bad application version length, exiting."); } /* * Determine which Kerberos CMD protocol was used. */ if (strncmp(auth_version.data, "KCMDV0.1", 9) == 0) { kcmd_protocol = KCMD_OLD_PROTOCOL; } else if (strncmp(auth_version.data, "KCMDV0.2", 9) == 0) { kcmd_protocol = KCMD_NEW_PROTOCOL; } else { syslog(LOG_ERR, "Unrecognized KCMD protocol (%s), exiting", (char *)auth_version.data); fatal(f, "Unrecognized KCMD protocol, exiting"); } if ((*auth_type == KRB5_RECVAUTH_V5) && chksum_flag && kcmd_protocol == KCMD_OLD_PROTOCOL) { if ((status = krb5_auth_con_getauthenticator(krb_context, auth_context, &authenticator))) return (status); if (authenticator->checksum) { struct sockaddr_storage adr; int adr_length = sizeof (adr); int buflen; krb5_data input; krb5_keyblock key; char *chksumbuf; /* * Define the lenght of the chksum buffer. * chksum string = "[portnum]:termstr:username" * The extra 32 is to hold a integer string for * the portnumber. */ buflen = strlen(term) + strlen(lusername) + 32; chksumbuf = (char *)malloc(buflen); if (chksumbuf == 0) { krb5_free_authenticator(krb_context, authenticator); fatal(f, "Out of memory error"); } if (getsockname(f, (struct sockaddr *)&adr, &adr_length) != 0) { krb5_free_authenticator(krb_context, authenticator); fatal(f, "getsockname error"); } (void) snprintf(chksumbuf, buflen, "%u:%s%s", ntohs(SOCK_PORT(adr)), term, lusername); input.data = chksumbuf; input.length = strlen(chksumbuf); key.contents = (*ticket)->enc_part2->session->contents; key.length = (*ticket)->enc_part2->session->length; status = krb5_c_verify_checksum(krb_context, &key, 0, &input, authenticator->checksum, valid_checksum); if (status == 0 && *valid_checksum == 0) status = KRB5KRB_AP_ERR_BAD_INTEGRITY; if (chksumbuf) krb5_xfree(chksumbuf); if (status) { krb5_free_authenticator(krb_context, authenticator); return (status); } } krb5_free_authenticator(krb_context, authenticator); } if ((status = krb5_copy_principal(krb_context, (*ticket)->enc_part2->client, client))) return (status); /* Get the Unix username of the remote user */ getstr(f, rusername, sizeof (rusername), "remuser"); /* Get the Kerberos principal name string of the remote user */ if ((status = krb5_unparse_name(krb_context, *client, &krusername))) return (status); #ifdef DEBUG syslog(LOG_DEBUG | LOG_AUTH, "rlogind: got krb5 credentials for %s", (krusername != NULL ? krusername : "******")); #endif if (encr_flag) { status = krb5_auth_con_getremotesubkey(krb_context, auth_context, &session_key); if (status) { syslog(LOG_ERR, "Error getting KRB5 session " "subkey, exiting"); fatal(f, "Error getting KRB5 session subkey, exiting"); } /* * The "new" protocol requires that a subkey be sent. */ if (session_key == NULL && kcmd_protocol == KCMD_NEW_PROTOCOL) { syslog(LOG_ERR, "No KRB5 session subkey sent, exiting"); fatal(f, "No KRB5 session subkey sent, exiting"); } /* * The "old" protocol does not permit an authenticator subkey. * The key is taken from the ticket instead (see below). */ if (session_key != NULL && kcmd_protocol == KCMD_OLD_PROTOCOL) { syslog(LOG_ERR, "KRB5 session subkey not permitted " "with old KCMD protocol, exiting"); fatal(f, "KRB5 session subkey not permitted " "with old KCMD protocol, exiting"); } /* * If no key at this point, use the session key from * the ticket. */ if (session_key == NULL) { /* * Save the session key so we can configure the crypto * module later. */ status = krb5_copy_keyblock(krb_context, (*ticket)->enc_part2->session, &session_key); if (status) { syslog(LOG_ERR, "krb5_copy_keyblock failed"); fatal(f, "krb5_copy_keyblock failed"); } } /* * If session key still cannot be found, we must * exit because encryption is required here * when encr_flag (-x) is set. */ if (session_key == NULL) { syslog(LOG_ERR, "Could not find an encryption key," "exiting"); fatal(f, "Encryption required but key not found, " "exiting"); } } /* * Use krb5_read_message to read the principal stuff. */ if ((status = krb5_read_message(krb_context, (krb5_pointer)&f, &inbuf))) fatal(f, "Error reading krb5 message"); if (inbuf.length) { /* Forwarding being done, read creds */ krb5_creds **creds = NULL; if (status = krb5_rd_cred(krb_context, auth_context, &inbuf, &creds, NULL)) { if (rcache) (void) krb5_rc_close(krb_context, rcache); krb5_free_creds(krb_context, *creds); fatal(f, "Can't get forwarded credentials"); } /* Store the forwarded creds in the ccache */ if (status = store_forw_creds(krb_context, creds, *ticket, lusername, &ccache)) { if (rcache) (void) krb5_rc_close(krb_context, rcache); krb5_free_creds(krb_context, *creds); fatal(f, "Can't store forwarded credentials"); } krb5_free_creds(krb_context, *creds); } if (rcache) (void) krb5_rc_close(krb_context, rcache); return (status); }
/* Given krb5 service name in KSSL_CTX *kssl_ctx (typically "kssl"), ** and krb5 AP_REQ message & message length, ** Return Kerberos session key and client principle ** to SSL Server in KSSL_CTX *kssl_ctx. ** ** 19990702 VRS Started. */ krb5_error_code kssl_sget_tkt( /* UPDATE */ KSSL_CTX *kssl_ctx, /* IN */ krb5_data *indata, /* OUT */ krb5_ticket_times *ttimes, /* OUT */ KSSL_ERR *kssl_err ) { krb5_error_code krb5rc = KRB5KRB_ERR_GENERIC; static krb5_context krb5context = NULL; static krb5_auth_context krb5auth_context = NULL; krb5_ticket *krb5ticket = NULL; KRB5_TKTBODY *asn1ticket = NULL; const unsigned char *p; krb5_keytab krb5keytab = NULL; krb5_keytab_entry kt_entry; krb5_principal krb5server; krb5_rcache rcache = NULL; kssl_err_set(kssl_err, 0, ""); if (!kssl_ctx) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "No kssl_ctx defined.\n"); goto err; } #ifdef KSSL_DEBUG printf("in kssl_sget_tkt(%s)\n", kstring(kssl_ctx->service_name)); #endif /* KSSL_DEBUG */ if (!krb5context && (krb5rc = krb5_init_context(&krb5context))) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_init_context() fails.\n"); goto err; } if (krb5auth_context && (krb5rc = krb5_auth_con_free(krb5context, krb5auth_context))) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_auth_con_free() fails.\n"); goto err; } else krb5auth_context = NULL; if (!krb5auth_context && (krb5rc = krb5_auth_con_init(krb5context, &krb5auth_context))) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_auth_con_init() fails.\n"); goto err; } if ((krb5rc = krb5_auth_con_getrcache(krb5context, krb5auth_context, &rcache))) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_auth_con_getrcache() fails.\n"); goto err; } if ((krb5rc = krb5_sname_to_principal(krb5context, NULL, (kssl_ctx->service_name) ? kssl_ctx->service_name : KRB5SVC, KRB5_NT_SRV_HST, &krb5server)) != 0) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_sname_to_principal() fails.\n"); goto err; } if (rcache == NULL) { if ((krb5rc = krb5_get_server_rcache(krb5context, krb5_princ_component(krb5context, krb5server, 0), &rcache))) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_get_server_rcache() fails.\n"); goto err; } } if ((krb5rc = krb5_auth_con_setrcache(krb5context, krb5auth_context, rcache))) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_auth_con_setrcache() fails.\n"); goto err; } /* kssl_ctx->keytab_file == NULL ==> use Kerberos default */ if (kssl_ctx->keytab_file) { krb5rc = krb5_kt_resolve(krb5context, kssl_ctx->keytab_file, &krb5keytab); if (krb5rc) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_kt_resolve() fails.\n"); goto err; } } else { krb5rc = krb5_kt_default(krb5context, &krb5keytab); if (krb5rc) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_kt_default() fails.\n"); goto err; } } /* Actual Kerberos5 krb5_recvauth() has initial conversation here ** o check KRB5_SENDAUTH_BADAUTHVERS ** unless KRB5_RECVAUTH_SKIP_VERSION ** o check KRB5_SENDAUTH_BADAPPLVERS ** o send "0" msg if all OK */ /* 20010411 was using AP_REQ instead of true KerberosWrapper ** ** if ((krb5rc = krb5_rd_req(krb5context, &krb5auth_context, ** &krb5in_data, krb5server, krb5keytab, ** &ap_option, &krb5ticket)) != 0) { Error } */ p = (unsigned char *)indata->data; if ((asn1ticket = (KRB5_TKTBODY *) d2i_KRB5_TICKET(NULL, &p, (long)indata->length)) == NULL) { (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "d2i_KRB5_TICKET() ASN.1 decode failure.\n"); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; goto err; } /* Was: krb5rc = krb5_decode_ticket(krb5in_data,&krb5ticket)) != 0) */ if ((krb5rc = kssl_TKT2tkt(krb5context, asn1ticket, &krb5ticket, kssl_err)) != 0) { (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "Error converting ASN.1 ticket to krb5_ticket.\n"); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; goto err; } if (!krb5_principal_compare(krb5context, krb5server, krb5ticket->server)) { krb5rc = KRB5_PRINC_NOMATCH; (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "server principal != ticket principal\n"); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; goto err; } if ((krb5rc = krb5_kt_get_entry(krb5context, krb5keytab, krb5ticket->server, krb5ticket->enc_part.kvno, krb5ticket->enc_part.enctype, &kt_entry)) != 0) { (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "krb5_kt_get_entry() fails with %x.\n", krb5rc); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; goto err; } if ((krb5rc = krb5_decrypt_tkt_part(krb5context, &kt_entry.key, krb5ticket)) != 0) { (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "krb5_decrypt_tkt_part() failed.\n"); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; goto err; } else { krb5_kt_free_entry(krb5context, &kt_entry); #ifdef KSSL_DEBUG { int i; krb5_address **paddr = krb5ticket->enc_part2->caddrs; printf("Decrypted ticket fields:\n"); printf("\tflags: %X, transit-type: %X", krb5ticket->enc_part2->flags, krb5ticket->enc_part2->transited.tr_type); print_krb5_data("\ttransit-data: ", &(krb5ticket->enc_part2->transited.tr_contents)); printf("\tcaddrs: %p, authdata: %p\n", krb5ticket->enc_part2->caddrs, krb5ticket->enc_part2->authorization_data); if (paddr) { printf("\tcaddrs:\n"); for (i = 0; paddr[i] != NULL; i++) { krb5_data d; d.length = paddr[i]->length; d.data = paddr[i]->contents; print_krb5_data("\t\tIP: ", &d); } } printf("\tstart/auth/end times: %d / %d / %d\n", krb5ticket->enc_part2->times.starttime, krb5ticket->enc_part2->times.authtime, krb5ticket->enc_part2->times.endtime); } #endif /* KSSL_DEBUG */ } krb5rc = KRB5_NO_TKT_SUPPLIED; if (!krb5ticket || !krb5ticket->enc_part2 || !krb5ticket->enc_part2->client || !krb5ticket->enc_part2->client->data || !krb5ticket->enc_part2->session) { kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET, "bad ticket from krb5_rd_req.\n"); } else if (kssl_ctx_setprinc(kssl_ctx, KSSL_CLIENT, &krb5ticket->enc_part2->client->realm, krb5ticket->enc_part2->client->data, krb5ticket->enc_part2->client->length)) { kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET, "kssl_ctx_setprinc() fails.\n"); } else if (kssl_ctx_setkey(kssl_ctx, krb5ticket->enc_part2->session)) { kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET, "kssl_ctx_setkey() fails.\n"); } else if (krb5ticket->enc_part2->flags & TKT_FLG_INVALID) { krb5rc = KRB5KRB_AP_ERR_TKT_INVALID; kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET, "invalid ticket from krb5_rd_req.\n"); } else krb5rc = 0; kssl_ctx->enctype = krb5ticket->enc_part.enctype; ttimes->authtime = krb5ticket->enc_part2->times.authtime; ttimes->starttime = krb5ticket->enc_part2->times.starttime; ttimes->endtime = krb5ticket->enc_part2->times.endtime; ttimes->renew_till = krb5ticket->enc_part2->times.renew_till; err: #ifdef KSSL_DEBUG kssl_ctx_show(kssl_ctx); #endif /* KSSL_DEBUG */ if (asn1ticket) KRB5_TICKET_free((KRB5_TICKET *) asn1ticket); if (krb5keytab) krb5_kt_close(krb5context, krb5keytab); if (krb5ticket) krb5_free_ticket(krb5context, krb5ticket); if (krb5server) krb5_free_principal(krb5context, krb5server); return (krb5rc); }
int kerberos5_is_auth (TN_Authenticator * ap, unsigned char *data, int cnt, char *errbuf, int errbuflen) { int r = 0; krb5_keytab keytabid = 0; krb5_authenticator *authenticator; char *name; krb5_data outbuf; krb5_keyblock *newkey = NULL; krb5_principal server; # ifdef ENCRYPTION Session_Key skey; # endif auth.data = (char *) data; auth.length = cnt; if (!r && !auth_context) r = krb5_auth_con_init (telnet_context, &auth_context); if (!r) { krb5_rcache rcache; r = krb5_auth_con_getrcache (telnet_context, auth_context, &rcache); if (!r && !rcache) { r = krb5_sname_to_principal (telnet_context, 0, 0, KRB5_NT_SRV_HST, &server); if (!r) { r = krb5_get_server_rcache (telnet_context, krb5_princ_component (telnet_context, server, 0), &rcache); krb5_free_principal (telnet_context, server); } } if (!r) r = krb5_auth_con_setrcache (telnet_context, auth_context, rcache); } if (!r && telnet_srvtab) r = krb5_kt_resolve (telnet_context, telnet_srvtab, &keytabid); if (!r) r = krb5_rd_req (telnet_context, &auth_context, &auth, NULL, keytabid, NULL, &ticket); if (r) { snprintf (errbuf, errbuflen, "krb5_rd_req failed: %s", error_message (r)); return r; } /* 256 bytes should be much larger than any reasonable first component of a service name especially since the default is of length 4. */ if (krb5_princ_component (telnet_context, ticket->server, 0)->length < 256) { char princ[256]; strncpy (princ, krb5_princ_component (telnet_context, ticket->server, 0)->data, krb5_princ_component (telnet_context, ticket->server, 0)->length); princ[krb5_princ_component (telnet_context, ticket->server, 0)-> length] = '\0'; if (strcmp ("host", princ)) { snprintf (errbuf, errbuflen, "incorrect service name: \"%s\" != \"host\"", princ); return 1; } } else { strncpy (errbuf, "service name too long", errbuflen); return 1; } r = krb5_auth_con_getauthenticator (telnet_context, auth_context, &authenticator); if (r) { snprintf (errbuf, errbuflen, "krb5_auth_con_getauthenticator failed: %s", error_message (r)); return 1; } # ifdef AUTH_ENCRYPT_MASK if ((ap->way & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON && !authenticator->checksum) { snprintf (errbuf, errbuflen, "authenticator is missing required checksum"); return 1; } # endif if (authenticator->checksum) { char type_check[2]; krb5_checksum *cksum = authenticator->checksum; krb5_keyblock *key; type_check[0] = ap->type; type_check[1] = ap->way; r = krb5_auth_con_getkey (telnet_context, auth_context, &key); if (r) { snprintf (errbuf, errbuflen, "krb5_auth_con_getkey failed: %s", error_message (r)); return 1; } r = krb5_verify_checksum (telnet_context, cksum->checksum_type, cksum, &type_check, 2, key->contents, key->length); if (r) { snprintf (errbuf, errbuflen, "checksum verification failed: %s", error_message (r)); return 1; } krb5_free_keyblock (telnet_context, key); } krb5_free_authenticator (telnet_context, authenticator); if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { if ((r = krb5_mk_rep (telnet_context, auth_context, &outbuf))) { snprintf (errbuf, errbuflen, "Make reply failed: %s", error_message (r)); return 1; } Data (ap, KRB_RESPONSE, outbuf.data, outbuf.length); } if (krb5_unparse_name (telnet_context, ticket->enc_part2->client, &name)) name = 0; Data (ap, KRB_ACCEPT, name, name ? -1 : 0); DEBUG (("telnetd: Kerberos5 identifies him as ``%s''\r\n", name ? name : "")); auth_finished (ap, AUTH_USER); free (name); krb5_auth_con_getremotesubkey (telnet_context, auth_context, &newkey); if (session_key) { krb5_free_keyblock (telnet_context, session_key); session_key = 0; } if (newkey) { krb5_copy_keyblock (telnet_context, newkey, &session_key); krb5_free_keyblock (telnet_context, newkey); } else { krb5_copy_keyblock (telnet_context, ticket->enc_part2->session, &session_key); } telnet_encrypt_key (&skey); return 0; }
int ksm_process_in_msg(struct snmp_secmod_incoming_params *parms) { long temp; krb5_cksumtype cksumtype; krb5_auth_context auth_context = NULL; krb5_error_code retcode; krb5_checksum checksum; krb5_data ap_req, ivector; krb5_flags flags; krb5_keyblock *subkey = NULL; #ifdef MIT_NEW_CRYPTO krb5_data input, output; krb5_boolean valid; krb5_enc_data in_crypt; #else /* MIT_NEW_CRYPTO */ krb5_encrypt_block eblock; #endif /* MIT_NEW_CRYPTO */ krb5_ticket *ticket = NULL; int retval = SNMPERR_SUCCESS, response = 0; size_t length = parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg); u_char *current = parms->secParams, type; size_t cksumlength, blocksize; long hint; char *cname; struct ksm_secStateRef *ksm_state; struct ksm_cache_entry *entry; DEBUGMSGTL(("ksm", "Processing has begun\n")); checksum.contents = NULL; ap_req.data = NULL; ivector.length = 0; ivector.data = NULL; /* * First, parse the security parameters (because we need the subkey inside * of the ticket to do anything */ if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm first octet")) == NULL) { DEBUGMSGTL(("ksm", "Initial security paramter parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } if ((current = asn_parse_sequence(current, &length, &type, (ASN_SEQUENCE | ASN_CONSTRUCTOR), "ksm sequence")) == NULL) { DEBUGMSGTL(("ksm", "Security parameter sequence parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } if ((current = asn_parse_int(current, &length, &type, &temp, sizeof(temp))) == NULL) { DEBUGMSGTL(("ksm", "Security parameter checksum type parsing" "failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } cksumtype = temp; #ifdef MIT_NEW_CRYPTO if (!krb5_c_valid_cksumtype(cksumtype)) { DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype)); retval = SNMPERR_KRB5; snmp_set_detail("Invalid checksum type"); goto error; } if (!krb5_c_is_keyed_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", cksumtype)); snmp_set_detail("Checksum is not a keyed checksum"); retval = SNMPERR_KRB5; goto error; } if (!krb5_c_is_coll_proof_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " "checksum\n", cksumtype)); snmp_set_detail("Checksum is not a collision-proof checksum"); retval = SNMPERR_KRB5; goto error; } #else /* ! MIT_NEW_CRYPTO */ if (!valid_cksumtype(cksumtype)) { DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype)); retval = SNMPERR_KRB5; snmp_set_detail("Invalid checksum type"); goto error; } if (!is_keyed_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", cksumtype)); snmp_set_detail("Checksum is not a keyed checksum"); retval = SNMPERR_KRB5; goto error; } if (!is_coll_proof_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " "checksum\n", cksumtype)); snmp_set_detail("Checksum is not a collision-proof checksum"); retval = SNMPERR_KRB5; goto error; } #endif /* MIT_NEW_CRYPTO */ checksum.checksum_type = cksumtype; cksumlength = length; if ((current = asn_parse_sequence(current, &cksumlength, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm checksum")) == NULL) { DEBUGMSGTL(("ksm", "Security parameter checksum parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } checksum.contents = malloc(cksumlength); if (!checksum.contents) { DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum.\n", cksumlength)); retval = SNMPERR_MALLOC; goto error; } memcpy(checksum.contents, current, cksumlength); checksum.length = cksumlength; checksum.checksum_type = cksumtype; /* * Zero out the checksum so the validation works correctly */ memset(current, 0, cksumlength); current += cksumlength; length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg); if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm ap_req")) == NULL) { DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing " "failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } ap_req.length = length; ap_req.data = malloc(length); if (!ap_req.data) { DEBUGMSGTL(("ksm", "KSM unable to malloc %d bytes for AP_REQ/REP.\n", length)); retval = SNMPERR_MALLOC; goto error; } memcpy(ap_req.data, current, length); current += length; length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg); if ((current = asn_parse_int(current, &length, &type, &hint, sizeof(hint))) == NULL) { DEBUGMSGTL(("ksm", "KSM security parameter hint parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } /* * Okay! We've got it all! Now try decoding the damn ticket. * * But of course there's a WRINKLE! We need to figure out if we're * processing a AP_REQ or an AP_REP. How do we do that? We're going * to cheat, and look at the first couple of bytes (which is what * the Kerberos library routines do anyway). * * If there are ever new Kerberos message formats, we'll need to fix * this here. * * If it's a _response_, then we need to get the auth_context * from our cache. */ if (ap_req.length && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) { /* * We need to initalize the authorization context, and set the * replay cache in it (and initialize the replay cache if we * haven't already */ retcode = krb5_auth_con_init(kcontext, &auth_context); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_init failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } if (!rcache) { krb5_data server; server.data = "host"; server.length = strlen(server.data); retcode = krb5_get_server_rcache(kcontext, &server, &rcache); if (retcode) { DEBUGMSGTL(("ksm", "krb5_get_server_rcache failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } } retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_setrcache failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL, keytab, &flags, &ticket); krb5_auth_con_setrcache(kcontext, auth_context, NULL); if (retcode) { DEBUGMSGTL(("ksm", "krb5_rd_req() failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname); if (retcode == 0) { DEBUGMSGTL(("ksm", "KSM authenticated principal name: %s\n", cname)); free(cname); } /* * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set */ if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) { DEBUGMSGTL(("ksm", "KSM MUTUAL_REQUIRED not set in request!\n")); retval = SNMPERR_KRB5; snmp_set_detail("MUTUAL_REQUIRED not set in message"); goto error; } retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM remote subkey retrieval failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } } else if (ap_req.length && (ap_req.data[0] == 0x6f || ap_req.data[0] == 0x4f)) { /* * Looks like a response; let's see if we've got that auth_context * in our cache. */ krb5_ap_rep_enc_part *repl = NULL; response = 1; entry = ksm_get_cache(parms->pdu->msgid); if (!entry) { DEBUGMSGTL(("ksm", "KSM: Unable to find auth_context for PDU with " "message ID of %ld\n", parms->pdu->msgid)); retval = SNMPERR_KRB5; goto error; } auth_context = entry->auth_context; /* * In that case, let's call the rd_rep function */ retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl); if (repl) krb5_free_ap_rep_enc_part(kcontext, repl); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.\n")); retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "Unable to retrieve local subkey: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail("Unable to retrieve local subkey"); goto error; } } else { DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n", ap_req.data[0])); retval = SNMPERR_KRB5; snmp_set_detail("Unknown Kerberos message type"); goto error; } #ifdef MIT_NEW_CRYPTO input.data = (char *) parms->wholeMsg; input.length = parms->wholeMsgLen; retcode = krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM, &input, &checksum, &valid); #else /* MIT_NEW_CRYPTO */ retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum, parms->wholeMsg, parms->wholeMsgLen, (krb5_pointer) subkey->contents, subkey->length); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "KSM checksum verification failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } /* * Don't ask me why they didn't simply return an error, but we have * to check to see if "valid" is false. */ #ifdef MIT_NEW_CRYPTO if (!valid) { DEBUGMSGTL(("ksm", "Computed checksum did not match supplied " "checksum!\n")); retval = SNMPERR_KRB5; snmp_set_detail ("Computed checksum did not match supplied checksum"); goto error; } #endif /* MIT_NEW_CRYPTO */ /* * Handle an encrypted PDU. Note that it's an OCTET_STRING of the * output of whatever Kerberos cryptosystem you're using (defined by * the encryption type). Note that this is NOT the EncryptedData * sequence - it's what goes in the "cipher" field of EncryptedData. */ if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm pdu")) == NULL) { DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } /* * The PDU is now pointed at by "current", and the length is in * "length". */ DEBUGMSGTL(("ksm", "KSM starting sPDU decode\n")); /* * We need to set up a blank initialization vector for the decryption. * Use a block of all zero's (which is dependent on the block size * of the encryption method). */ #ifdef MIT_NEW_CRYPTO retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize); if (retcode) { DEBUGMSGTL(("ksm", "Unable to determine crypto block size: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #else /* MIT_NEW_CRYPTO */ blocksize = krb5_enctype_array[subkey->enctype]->system->block_length; #endif /* MIT_NEW_CRYPTO */ ivector.data = malloc(blocksize); if (!ivector.data) { DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n", blocksize)); retval = SNMPERR_MALLOC; goto error; } ivector.length = blocksize; memset(ivector.data, 0, blocksize); #ifndef MIT_NEW_CRYPTO krb5_use_enctype(kcontext, &eblock, subkey->enctype); retcode = krb5_process_key(kcontext, &eblock, subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM key post-processing failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #endif /* !MIT_NEW_CRYPTO */ if (length > *parms->scopedPduLen) { DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to " "decrypt but only %d bytes available\n", length, *parms->scopedPduLen)); retval = SNMPERR_TOO_LONG; #ifndef MIT_NEW_CRYPTO krb5_finish_key(kcontext, &eblock); #endif /* ! MIT_NEW_CRYPTO */ goto error; } #ifdef MIT_NEW_CRYPTO in_crypt.ciphertext.data = (char *) current; in_crypt.ciphertext.length = length; in_crypt.enctype = subkey->enctype; output.data = (char *) *parms->scopedPdu; output.length = *parms->scopedPduLen; retcode = krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION, &ivector, &in_crypt, &output); #else /* MIT_NEW_CRYPTO */ retcode = krb5_decrypt(kcontext, (krb5_pointer) current, *parms->scopedPdu, length, &eblock, ivector.data); krb5_finish_key(kcontext, &eblock); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "Decryption failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } *parms->scopedPduLen = length; } else { /* * Clear PDU */ *parms->scopedPdu = current; *parms->scopedPduLen = parms->wholeMsgLen - (current - parms->wholeMsg); } /* * A HUGE GROSS HACK */ *parms->maxSizeResponse = parms->maxMsgSize - 200; DEBUGMSGTL(("ksm", "KSM processing complete\n")); /* * Set the secName to the right value (a hack for now). But that's * only used for when we're processing a request, not a response. */ if (!response) { retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname); if (retcode) { DEBUGMSGTL(("ksm", "KSM krb5_unparse_name failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } if (strlen(cname) > *parms->secNameLen + 1) { DEBUGMSGTL(("ksm", "KSM: Principal length (%d) is too long (%d)\n", strlen(cname), parms->secNameLen)); retval = SNMPERR_TOO_LONG; free(cname); goto error; } strcpy(parms->secName, cname); *parms->secNameLen = strlen(cname); free(cname); /* * Also, if we're not a response, keep around our auth_context so we * can encode the reply message correctly */ ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef); if (!ksm_state) { DEBUGMSGTL(("ksm", "KSM unable to malloc memory for " "ksm_secStateRef\n")); retval = SNMPERR_MALLOC; goto error; } ksm_state->auth_context = auth_context; auth_context = NULL; ksm_state->cksumtype = cksumtype; *parms->secStateRef = ksm_state; } else { /* * We _still_ have to set the secName in process_in_msg(). Do * that now with what we were passed in before (we cached it, * remember?) */ memcpy(parms->secName, entry->secName, entry->secNameLen); *parms->secNameLen = entry->secNameLen; } /* * Just in case */ parms->secEngineID = (u_char *) ""; *parms->secEngineIDLen = 0; auth_context = NULL; /* So we don't try to free it on success */ error: if (retval == SNMPERR_ASN_PARSE_ERR && snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0) DEBUGMSGTL(("ksm", "Failed to increment statistics.\n")); if (subkey) krb5_free_keyblock(kcontext, subkey); if (checksum.contents) free(checksum.contents); if (ivector.data) free(ivector.data); if (ticket) krb5_free_ticket(kcontext, ticket); if (!response && auth_context) krb5_auth_con_free(kcontext, auth_context); if (ap_req.data) free(ap_req.data); return retval; }