int write_heimdal_enc_key(char **p, char *end, gss_ctx_id_t ctx) { krb5_keyblock enc_key, *key; krb5_context context; krb5_error_code ret; int i; char *skd, *dkd, *k5err = NULL; int code = -1; if ((ret = krb5_init_context(&context))) { k5err = gssd_k5_err_msg(NULL, ret); printerr(0, "ERROR: initializing krb5_context: %s\n", k5err); goto out_err; } if ((ret = krb5_auth_con_getlocalsubkey(context, ctx->auth_context, &key))){ k5err = gssd_k5_err_msg(context, ret); printerr(0, "ERROR: getting auth_context key: %s\n", k5err); goto out_err_free_context; } memset(&enc_key, 0, sizeof(enc_key)); enc_key.keytype = key->keytype; /* XXX current kernel code only handles des-cbc-raw (4) */ if (enc_key.keytype != 4) { printerr(1, "WARN: write_heimdal_enc_key: " "overriding heimdal keytype (%d => %d)\n", enc_key.keytype, 4); enc_key.keytype = 4; } enc_key.keyvalue.length = key->keyvalue.length; if ((enc_key.keyvalue.data = calloc(1, enc_key.keyvalue.length)) == NULL) { k5err = gssd_k5_err_msg(context, ENOMEM); printerr(0, "ERROR: allocating memory for enc key: %s\n", k5err); goto out_err_free_key; } skd = (char *) key->keyvalue.data; dkd = (char *) enc_key.keyvalue.data; for (i = 0; i < enc_key.keyvalue.length; i++) dkd[i] = skd[i] ^ 0xf0; if (write_heimdal_keyblock(p, end, &enc_key)) { goto out_err_free_enckey; } code = 0; out_err_free_enckey: krb5_free_keyblock_contents(context, &enc_key); out_err_free_key: krb5_free_keyblock(context, key); out_err_free_context: krb5_free_context(context); out_err: free(k5err); printerr(2, "write_heimdal_enc_key: %s\n", code ? "FAILED" : "SUCCESS"); return(code); }
/* * smb_krb5_principal_get_realm * * @brief Get realm of a principal * * @param[in] context The krb5_context * @param[in] principal The principal * @return pointer to the realm * */ static char *cifs_krb5_principal_get_realm(krb5_context context __attribute__ ((unused)), krb5_principal principal) { #ifdef HAVE_KRB5_PRINCIPAL_GET_REALM /* Heimdal */ return krb5_principal_get_realm(context, principal); #elif defined(krb5_princ_realm) /* MIT */ krb5_data *realm; realm = krb5_princ_realm(context, principal); return (char *)realm->data; #else return NULL; #endif } #if !defined(HAVE_KRB5_FREE_UNPARSED_NAME) static void krb5_free_unparsed_name(krb5_context context, char *val) { SAFE_FREE(val); } #endif #if !defined(HAVE_KRB5_AUTH_CON_GETSENDSUBKEY) /* Heimdal */ static krb5_error_code krb5_auth_con_getsendsubkey(krb5_context context, krb5_auth_context auth_context, krb5_keyblock **keyblock) { return krb5_auth_con_getlocalsubkey(context, auth_context, keyblock); }
/* * @Description: get session key from TGS_REP * @param: input auth context * @output: session key * @return * */ int get_krb5_smb_session_key(/* TALLOC_CTX *mem_ctx, krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, bool remote=false*/) { /*get local subkey because remote = false*/ krb5_auth_con_getlocalsubkey(); }
int write_heimdal_seq_key(char **p, char *end, gss_ctx_id_t ctx) { krb5_keyblock *key; krb5_context context; krb5_error_code ret; char *k5err = NULL; int code = -1; if ((ret = krb5_init_context(&context))) { k5err = gssd_k5_err_msg(NULL, ret); printerr(0, "ERROR: initializing krb5_context: %s\n", k5err); goto out_err; } if ((ret = krb5_auth_con_getlocalsubkey(context, ctx->auth_context, &key))){ k5err = gssd_k5_err_msg(context, ret); printerr(0, "ERROR: getting auth_context key: %s\n", k5err); goto out_err_free_context; } /* XXX current kernel code only handles des-cbc-raw (4) */ if (key->keytype != 4) { printerr(1, "WARN: write_heimdal_seq_key: " "overriding heimdal keytype (%d => %d)\n", key->keytype, 4); key->keytype = 4; } if (write_heimdal_keyblock(p, end, key)) { goto out_err_free_key; } code = 0; out_err_free_key: krb5_free_keyblock(context, key); out_err_free_context: krb5_free_context(context); out_err: free(k5err); printerr(2, "write_heimdal_seq_key: %s\n", code ? "FAILED" : "SUCCESS"); return(code); }
static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security, DATA_BLOB *session_key) { struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context; krb5_auth_context auth_context = gensec_krb5_state->auth_context; krb5_keyblock *skey; krb5_error_code err = -1; if (gensec_krb5_state->state_position != GENSEC_KRB5_DONE) { return NT_STATUS_NO_USER_SESSION_KEY; } if (gensec_krb5_state->session_key.data) { *session_key = gensec_krb5_state->session_key; return NT_STATUS_OK; } switch (gensec_security->gensec_role) { case GENSEC_CLIENT: err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey); break; case GENSEC_SERVER: err = krb5_auth_con_getremotesubkey(context, auth_context, &skey); break; } if (err == 0 && skey != NULL) { DEBUG(10, ("Got KRB5 session key of length %d\n", (int)KRB5_KEY_LENGTH(skey))); gensec_krb5_state->session_key = data_blob_talloc(gensec_krb5_state, KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey)); *session_key = gensec_krb5_state->session_key; dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length); krb5_free_keyblock(context, skey); return NT_STATUS_OK; } else { DEBUG(10, ("KRB5 error getting session key %d\n", err)); return NT_STATUS_NO_USER_SESSION_KEY; } }
void encryption_init (krb5_creds * creds) { krb5_keyblock *newkey = 0; krb5_auth_con_getlocalsubkey (telnet_context, auth_context, &newkey); if (session_key) { krb5_free_keyblock (telnet_context, session_key); session_key = 0; } if (newkey) { switch (newkey->enctype) { case ENCTYPE_DES_CBC_CRC: case ENCTYPE_DES_CBC_MD5: krb5_copy_keyblock (telnet_context, newkey, &session_key); break; default: switch (creds->keyblock.enctype) { case ENCTYPE_DES_CBC_CRC: case ENCTYPE_DES_CBC_MD5: krb5_copy_keyblock (telnet_context, &creds->keyblock, &session_key); break; default: DEBUG (("can't determine which keyblock to use")); /*FIXME: abort? */ } } krb5_free_keyblock (telnet_context, newkey); } }
BOOL get_krb5_smb_session_key(krb5_context context, krb5_auth_context auth_context, DATA_BLOB *session_key, BOOL remote) { krb5_keyblock *skey; krb5_error_code err; BOOL ret = False; if (remote) err = krb5_auth_con_getremotesubkey(context, auth_context, &skey); else err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey); if (err == 0 && skey != NULL) { DEBUG(10, ("Got KRB5 session key of length %d\n", (int)KRB5_KEY_LENGTH(skey))); *session_key = data_blob(KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey)); dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length); ret = True; krb5_free_keyblock(context, skey); } else { DEBUG(10, ("KRB5 error getting session key %d\n", err)); } return ret; }
int main(int argc, char **argv) { int c; char *cp, *cmd, *name = NULL; struct passwd *pwd; uid_t uid; int options = 0, oldmask; int on = 1; speed_t speed = 0; int getattr_ret; char *tmp; int sock; krb5_flags authopts; krb5_error_code status; enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) { (void) fprintf(stderr, gettext("Insufficient privileges, " "rlogin must be set-uid root\n")); exit(1); } { int it; if ((getattr_ret = tcgetattr(STDIN_FILENO, &savetty)) < 0) perror("tcgetattr"); it = ioctl(STDIN_FILENO, I_FIND, "ttcompat"); if (it < 0) { perror("ioctl I_FIND ttcompat"); return (EXIT_FAILURE); } if (it == 0) { if (ioctl(STDIN_FILENO, I_PUSH, "ttcompat") < 0) { perror("ioctl I_PUSH ttcompat"); exit(EXIT_FAILURE); } ttcompat = B_TRUE; } } /* * Determine command name used to invoke to rlogin(1). Users can * create links named by a host pointing to the binary and type * "hostname" to log into that host afterwards. */ cmd = strrchr(argv[0], '/'); cmd = (cmd != NULL) ? (cmd + 1) : argv[0]; if (strcmp(cmd, rlogin) == 0) { if (argc < 2) usage(); if (*argv[1] != '-') { host = argv[1]; argc--; argv[1] = argv[0]; argv++; } } else { host = cmd; } while ((c = getopt(argc, argv, DEBUGOPTSTRING "8AEFLP:ade:fk:l:x")) != -1) { switch (c) { case '8': eight = B_TRUE; break; case 'A': krb5auth_flag = B_TRUE; break; #ifdef DEBUG case 'D': portnumber = htons(atoi(optarg)); krb5auth_flag = B_TRUE; break; #endif /* DEBUG */ case 'E': nocmdchar = B_TRUE; break; case 'F': if (fflag) usage_forward(); Fflag = 1; krb5auth_flag = B_TRUE; fwdable_done = B_TRUE; break; case 'f': if (Fflag) usage_forward(); fflag = 1; krb5auth_flag = B_TRUE; fwd_done = B_TRUE; break; case 'L': litout = B_TRUE; break; case 'P': if (strcmp(optarg, "N") == 0) kcmd_proto = KCMD_NEW_PROTOCOL; else if (strcmp(optarg, "O") == 0) kcmd_proto = KCMD_OLD_PROTOCOL; else die(gettext("rlogin: Only -PN or -PO " "allowed.\n")); if (rcmdoption_done) die(gettext("rlogin: Only one of -PN and -PO " "allowed.\n")); rcmdoption_done = B_TRUE; krb5auth_flag = B_TRUE; break; case 'a': /* * Force the remote host to prompt for a password by sending * a NULL username. This option is mutually exclusive with * the -A, -x, -f, -F, -k <realm> options. */ null_local_username = B_TRUE; break; case 'd': options |= SO_DEBUG; break; case 'e': { int c; cp = optarg; if ((c = *cp) != '\\') { cmdchar = c; } else { c = cp[1]; if (c == '\0' || c == '\\') { cmdchar = '\\'; } else if (c >= '0' && c <= '7') { long lc; lc = strtol(&cp[1], NULL, 8); if (lc < 0 || lc > 255) die(gettext("rlogin: octal " "escape character %s too " "large.\n"), cp); cmdchar = (char)lc; } else { die(gettext("rlogin: unrecognized " "escape character option %s.\n"), cp); } } break; } case 'k': krb_realm = optarg; krb5auth_flag = B_TRUE; break; case 'l': name = optarg; break; case 'x': encrypt_flag = 1; krb5auth_flag = B_TRUE; encrypt_done = B_TRUE; break; default: usage(); } } argc -= optind; argv += optind; if (host == NULL) { if (argc == 0) usage(); argc--; host = *argv++; } if (argc > 0) usage(); pwd = getpwuid(uid = getuid()); if (pwd == NULL) { (void) fprintf(stderr, gettext("getpwuid(): can not find " "password entry for user id %d."), uid); return (EXIT_FAILURE); } if (name == NULL) name = pwd->pw_name; /* * If the `-a' option is issued on the cmd line, we reset all * flags associated with other KRB5 specific options, since * the -a option is mutually exclusive with the rest. */ if (null_local_username) { krb5auth_flag = B_FALSE; fflag = Fflag = encrypt_flag = 0; (void) fprintf(stderr, gettext("Note: The -a option nullifies " "all other Kerberos-specific\noptions " "you may have used.\n")); } if (krb5auth_flag) { status = krb5_init_context(&bsd_context); if (status) { com_err(rlogin, status, gettext("while initializing" " krb5")); return (EXIT_FAILURE); } /* * Set up buffers for desread and deswrite. */ desinbuf.data = des_inbuf; desoutbuf.data = des_outbuf; desinbuf.length = sizeof (des_inbuf); desoutbuf.length = sizeof (des_outbuf); /* * Get our local realm to look up local realm options. */ status = krb5_get_default_realm(bsd_context, &realmdef[1]); if (status) { com_err(rlogin, status, gettext("while getting default realm")); return (EXIT_FAILURE); } /* * Check the realms section in krb5.conf for encryption, * forward & forwardable info */ profile_get_options_boolean(bsd_context->profile, realmdef, option); /* * Check the appdefaults section */ profile_get_options_boolean(bsd_context->profile, appdef, option); profile_get_options_string(bsd_context->profile, appdef, rcmdversion); /* * Set the *_flag variables, if the corresponding *_done are * set to 1, because we dont want the config file values * overriding the command line options. */ if (encrypt_done) encrypt_flag = 1; if (fwd_done) { fflag = 1; Fflag = 0; } else if (fwdable_done) { Fflag = 1; fflag = 0; } if (!rcmdoption_done && (rcmdproto != NULL)) { if (strncmp(rcmdproto, "rcmdv2", 6) == 0) { kcmd_proto = KCMD_NEW_PROTOCOL; } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) { kcmd_proto = KCMD_OLD_PROTOCOL; } else { (void) fprintf(stderr, gettext("Unrecognized " "KCMD protocol (%s)"), rcmdproto); return (EXIT_FAILURE); } } if (encrypt_flag && (!krb5_privacy_allowed())) { (void) fprintf(stderr, gettext("rlogin: "******"Encryption not supported.\n")); return (EXIT_FAILURE); } } if (port_number == 0) { if (krb5auth_flag) { struct servent *sp; /* * If the krb5auth_flag is set (via -A, -f, -F, -k) & * if there is an entry in /etc/services for Kerberos * login, attempt to login with Kerberos. If we fail * at any step, use the standard rlogin */ sp = getservbyname(encrypt_flag ? "eklogin" : "klogin", "tcp"); if (sp == NULL) { port_number = encrypt_flag ? htons(2105) : htons(543); } else { port_number = sp->s_port; } } else { port_number = htons(IPPORT_LOGINSERVER); } } cp = getenv("TERM"); if (cp) { (void) strncpy(term, cp, sizeof (term)); term[sizeof (term) - 1] = '\0'; } if (getattr_ret == 0) { speed = cfgetospeed(&savetty); /* * "Be conservative in what we send" -- Only send baud rates * which at least all 4.x BSD derivatives are known to handle * correctly. * NOTE: This code assumes new termios speed values will * be "higher" speeds. */ if (speed > B38400) speed = B38400; } /* * Only put the terminal speed info in if we have room * so we don't overflow the buffer, and only if we have * a speed we recognize. */ if (speed > 0 && speed < sizeof (speeds)/sizeof (char *) && strlen(term) + strlen("/") + strlen(speeds[speed]) + 1 < sizeof (term)) { (void) strcat(term, "/"); (void) strcat(term, speeds[speed]); } (void) sigset(SIGPIPE, (sigdisp_t)lostpeer); /* will use SIGUSR1 for window size hack, so hold it off */ oldmask = sigblock(sigmask(SIGURG) | sigmask(SIGUSR1)); /* * Determine if v4 literal address and if so store it to one * side. This is to correct the undesired behaviour of rcmd_af * which converts a passed in v4 literal address to a v4 mapped * v6 literal address. If it was a v4 literal we then re-assign * it to host. */ tmp = NULL; if (inet_addr(host) != (in_addr_t)-1) tmp = host; if (krb5auth_flag) { authopts = AP_OPTS_MUTUAL_REQUIRED; /* Piggy-back forwarding flags on top of authopts; */ /* they will be reset in kcmd */ if (fflag || Fflag) authopts |= OPTS_FORWARD_CREDS; if (Fflag) authopts |= OPTS_FORWARDABLE_CREDS; status = kcmd(&sock, &host, port_number, null_local_username ? "" : pwd->pw_name, name, term, NULL, "host", krb_realm, bsd_context, &auth_context, &cred, NULL, /* No need for sequence number */ NULL, /* No need for server seq # */ authopts, 0, /* Not any port # */ &kcmd_proto); if (status != 0) { /* * If new protocol requested, we dont fallback to * less secure ones. */ if (kcmd_proto == KCMD_NEW_PROTOCOL) { (void) fprintf(stderr, gettext("rlogin: kcmdv2 " "to host %s failed - %s\n" "Fallback to normal rlogin denied."), host, error_message(status)); return (EXIT_FAILURE); } if (status != -1) { (void) fprintf(stderr, gettext("rlogin: kcmd " "to host %s failed - %s,\n" "trying normal rlogin...\n\n"), host, error_message(status)); } else { (void) fprintf(stderr, gettext("trying normal rlogin...\n")); } /* * kcmd() failed, so we have to * fallback to normal rlogin */ port_number = htons(IPPORT_LOGINSERVER); krb5auth_flag = B_FALSE; fflag = Fflag = encrypt_flag = 0; null_local_username = B_FALSE; } else { (void) fprintf(stderr, gettext("connected with Kerberos V5\n")); /* * Setup eblock for desread and deswrite. */ session_key = &cred->keyblock; if (kcmd_proto == KCMD_NEW_PROTOCOL) { status = krb5_auth_con_getlocalsubkey( bsd_context, auth_context, &session_key); if (status) { com_err(rlogin, status, "determining subkey for session"); return (EXIT_FAILURE); } if (session_key == NULL) { com_err(rlogin, 0, "no subkey negotiated for " "connection"); return (EXIT_FAILURE); } } eblock.crypto_entry = session_key->enctype; eblock.key = (krb5_keyblock *)session_key; init_encrypt(encrypt_flag, bsd_context, kcmd_proto, &desinbuf, &desoutbuf, CLIENT, &eblock); rem = sock; if (rem < 0) pop(EXIT_FAILURE); } } /* * Don't merge this with the "if" statement above because * "krb5auth_flag" might be set to false inside it. */ if (!krb5auth_flag) { rem = rcmd_af(&host, port_number, null_local_username ? "" : pwd->pw_name, name, term, NULL, AF_INET6); if (rem < 0) pop(EXIT_FAILURE); } /* Never need our privilege again */ __priv_relinquish(); if (tmp != NULL) host = tmp; if (options & SO_DEBUG && setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0) perror("rlogin: setsockopt (SO_DEBUG)"); { int bufsize = 8192; (void) setsockopt(rem, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof (int)); } doit(oldmask); return (0); }
void kerberos5_reply(Authenticator *ap, unsigned char *data, int cnt) { static int mutual_complete = 0; if (cnt-- < 1) return; switch (*data++) { case KRB_REJECT: if (cnt > 0) { printf("[ Kerberos V5 refuses authentication because %.*s ]\r\n", cnt, data); } else printf("[ Kerberos V5 refuses authentication ]\r\n"); auth_send_retry(); return; case KRB_ACCEPT: { krb5_error_code ret; Session_Key skey; krb5_keyblock *keyblock; if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL && !mutual_complete) { printf("[ Kerberos V5 accepted you, but didn't provide mutual authentication! ]\r\n"); auth_send_retry(); return; } if (cnt) printf("[ Kerberos V5 accepts you as ``%.*s'' ]\r\n", cnt, data); else printf("[ Kerberos V5 accepts you ]\r\n"); ret = krb5_auth_con_getlocalsubkey (context, auth_context, &keyblock); if (ret) ret = krb5_auth_con_getkey (context, auth_context, &keyblock); if(ret) { printf("[ krb5_auth_con_getkey: %s ]\r\n", krb5_get_err_text(context, ret)); auth_send_retry(); return; } skey.type = SK_DES; skey.length = 8; skey.data = keyblock->keyvalue.data; encrypt_session_key(&skey, 0); krb5_free_keyblock_contents (context, keyblock); auth_finished(ap, AUTH_USER); #ifdef FORWARD if (forward_flags & OPTS_FORWARD_CREDS) kerberos5_forward(ap); #endif /* FORWARD */ break; } case KRB_RESPONSE: if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { /* the rest of the reply should contain a krb_ap_rep */ krb5_ap_rep_enc_part *reply; krb5_data inbuf; krb5_error_code ret; inbuf.length = cnt; inbuf.data = (char *)data; ret = krb5_rd_rep(context, auth_context, &inbuf, &reply); if (ret) { printf("[ Mutual authentication failed: %s ]\r\n", krb5_get_err_text (context, ret)); auth_send_retry(); return; } krb5_free_ap_rep_enc_part(context, reply); mutual_complete = 1; } return; #ifdef FORWARD case KRB_FORWARD_ACCEPT: printf("[ Kerberos V5 accepted forwarded credentials ]\r\n"); return; case KRB_FORWARD_REJECT: printf("[ Kerberos V5 refuses forwarded credentials because %.*s ]\r\n", cnt, data); return; #endif /* FORWARD */ default: if (auth_debug_mode) printf("Unknown Kerberos option %d\r\n", data[-1]); return; } }
static krb5_error_code digest_request(krb5_context context, krb5_realm realm, krb5_ccache ccache, krb5_key_usage usage, const DigestReqInner *ireq, DigestRepInner *irep) { DigestREQ req; DigestREP rep; krb5_error_code ret; krb5_data data, data2; size_t size = 0; krb5_crypto crypto = NULL; krb5_auth_context ac = NULL; krb5_principal principal = NULL; krb5_ccache id = NULL; krb5_realm r = NULL; krb5_data_zero(&data); krb5_data_zero(&data2); memset(&req, 0, sizeof(req)); memset(&rep, 0, sizeof(rep)); if (ccache == NULL) { ret = krb5_cc_default(context, &id); if (ret) goto out; } else id = ccache; if (realm == NULL) { ret = krb5_get_default_realm(context, &r); if (ret) goto out; } else r = realm; /* * */ ret = krb5_make_principal(context, &principal, r, KRB5_DIGEST_NAME, r, NULL); if (ret) goto out; ASN1_MALLOC_ENCODE(DigestReqInner, data.data, data.length, ireq, &size, ret); if (ret) { krb5_set_error_message(context, ret, N_("Failed to encode digest inner request", "")); goto out; } if (size != data.length) krb5_abortx(context, "ASN.1 internal encoder error"); ret = krb5_mk_req_exact(context, &ac, AP_OPTS_USE_SUBKEY|AP_OPTS_MUTUAL_REQUIRED, principal, NULL, id, &req.apReq); if (ret) goto out; { krb5_keyblock *key; ret = krb5_auth_con_getlocalsubkey(context, ac, &key); if (ret) goto out; if (key == NULL) { ret = EINVAL; krb5_set_error_message(context, ret, N_("Digest failed to get local subkey", "")); goto out; } ret = krb5_crypto_init(context, key, 0, &crypto); krb5_free_keyblock (context, key); if (ret) goto out; } ret = krb5_encrypt_EncryptedData(context, crypto, usage, data.data, data.length, 0, &req.innerReq); if (ret) goto out; krb5_data_free(&data); ASN1_MALLOC_ENCODE(DigestREQ, data.data, data.length, &req, &size, ret); if (ret) { krb5_set_error_message(context, ret, N_("Failed to encode DigestREQest", "")); goto out; } if (size != data.length) krb5_abortx(context, "ASN.1 internal encoder error"); ret = krb5_sendto_kdc(context, &data, &r, &data2); if (ret) goto out; ret = decode_DigestREP(data2.data, data2.length, &rep, NULL); if (ret) { krb5_set_error_message(context, ret, N_("Failed to parse digest response", "")); goto out; } { krb5_ap_rep_enc_part *repl; ret = krb5_rd_rep(context, ac, &rep.apRep, &repl); if (ret) goto out; krb5_free_ap_rep_enc_part(context, repl); } { krb5_keyblock *key; ret = krb5_auth_con_getremotesubkey(context, ac, &key); if (ret) goto out; if (key == NULL) { ret = EINVAL; krb5_set_error_message(context, ret, N_("Digest reply have no remote subkey", "")); goto out; } krb5_crypto_destroy(context, crypto); ret = krb5_crypto_init(context, key, 0, &crypto); krb5_free_keyblock (context, key); if (ret) goto out; } krb5_data_free(&data); ret = krb5_decrypt_EncryptedData(context, crypto, usage, &rep.innerRep, &data); if (ret) goto out; ret = decode_DigestRepInner(data.data, data.length, irep, NULL); if (ret) { krb5_set_error_message(context, ret, N_("Failed to decode digest inner reply", "")); goto out; } out: if (ccache == NULL && id) krb5_cc_close(context, id); if (realm == NULL && r) free(r); if (crypto) krb5_crypto_destroy(context, crypto); if (ac) krb5_auth_con_free(context, ac); if (principal) krb5_free_principal(context, principal); krb5_data_free(&data); krb5_data_free(&data2); free_DigestREQ(&req); free_DigestREP(&rep); return ret; }
int ksm_rgenerate_out_msg(struct snmp_secmod_outgoing_params *parms) { krb5_auth_context auth_context = NULL; krb5_error_code retcode; krb5_ccache cc = NULL; int retval = SNMPERR_SUCCESS; krb5_data outdata, ivector; krb5_keyblock *subkey = NULL; #ifdef MIT_NEW_CRYPTO krb5_data input; krb5_enc_data output; unsigned int numcksumtypes; krb5_cksumtype *cksumtype_array; #else /* MIT_NEW_CRYPTO */ krb5_encrypt_block eblock; #endif /* MIT_NEW_CRYPTO */ size_t blocksize, encrypted_length; unsigned char *encrypted_data = NULL; int zero = 0, i; u_char *cksum_pointer, *endp = *parms->wholeMsg; krb5_cksumtype cksumtype; krb5_checksum pdu_checksum; u_char **wholeMsg = parms->wholeMsg; size_t *offset = parms->wholeMsgOffset, seq_offset; struct ksm_secStateRef *ksm_state = (struct ksm_secStateRef *) parms->secStateRef; int rc; DEBUGMSGTL(("ksm", "Starting KSM processing\n")); outdata.length = 0; outdata.data = NULL; ivector.length = 0; ivector.data = NULL; pdu_checksum.contents = NULL; if (!ksm_state) { /* * If we don't have a ksm_state, then we're a request. Get a * credential cache and build a ap_req. */ retcode = krb5_cc_default(kcontext, &cc); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_cc_default failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: Set credential cache successfully\n")); /* * This seems odd, since we don't need this until later (or earlier, * depending on how you look at it), but because the most likely * errors are Kerberos at this point, I'll get this now to save * time not encoding the rest of the packet. * * Also, we need the subkey to encrypt the PDU (if required). */ retcode = krb5_mk_req(kcontext, &auth_context, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, (char *) service_name, parms->session->peername, NULL, cc, &outdata); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_mk_req failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: ticket retrieved successfully for \"%s/%s\" " "(may not be actual ticket sname)\n", service_name, parms->session->peername)); } else { /* * Grab the auth_context from our security state reference */ auth_context = ksm_state->auth_context; /* * Bundle up an AP_REP. Note that we do this only when we * have a security state reference (which means we're in an agent * and we're sending a response). */ DEBUGMSGTL(("ksm", "KSM: Starting reply processing.\n")); retcode = krb5_mk_rep(kcontext, auth_context, &outdata); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_mk_rep failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: Finished with krb5_mk_rep()\n")); } /* * If we have to encrypt the PDU, do that now */ if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { DEBUGMSGTL(("ksm", "KSM: Starting PDU encryption.\n")); /* * It's weird - * * If we're on the manager, it's a local subkey (because that's in * our AP_REQ) * * If we're on the agent, it's a remote subkey (because that comes * FROM the received AP_REQ). */ if (ksm_state) retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey); else retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_auth_con_getlocalsubkey failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } /* * Note that here we need to handle different things between the * old and new crypto APIs. First, we need to get the final encrypted * length of the PDU. */ #ifdef MIT_NEW_CRYPTO retcode = krb5_c_encrypt_length(kcontext, subkey->enctype, parms->scopedPduLen, &encrypted_length); if (retcode) { DEBUGMSGTL(("ksm", "Encryption length calculation failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #else /* MIT_NEW_CRYPTO */ krb5_use_enctype(kcontext, &eblock, subkey->enctype); retcode = krb5_process_key(kcontext, &eblock, subkey); if (retcode) { DEBUGMSGTL(("ksm", "krb5_process_key failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } encrypted_length = krb5_encrypt_size(parms->scopedPduLen, eblock.crypto_entry); #endif /* MIT_NEW_CRYPTO */ encrypted_data = malloc(encrypted_length); if (!encrypted_data) { DEBUGMSGTL(("ksm", "KSM: Unable to malloc %d bytes for encrypt " "buffer: %s\n", parms->scopedPduLen, strerror(errno))); retval = SNMPERR_MALLOC; #ifndef MIT_NEW_CRYPTO krb5_finish_key(kcontext, &eblock); #endif /* ! MIT_NEW_CRYPTO */ goto error; } /* * We need to set up a blank initialization vector for the encryption. * 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); /* * Finally! Do the encryption! */ #ifdef MIT_NEW_CRYPTO input.data = (char *) parms->scopedPdu; input.length = parms->scopedPduLen; output.ciphertext.data = (char *) encrypted_data; output.ciphertext.length = encrypted_length; retcode = krb5_c_encrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION, &ivector, &input, &output); #else /* MIT_NEW_CRYPTO */ retcode = krb5_encrypt(kcontext, (krb5_pointer) parms->scopedPdu, (krb5_pointer) encrypted_data, parms->scopedPduLen, &eblock, ivector.data); krb5_finish_key(kcontext, &eblock); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_encrypt failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } *offset = 0; rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen, offset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), encrypted_data, encrypted_length); if (rc == 0) { DEBUGMSGTL(("ksm", "Building encrypted payload failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } DEBUGMSGTL(("ksm", "KSM: Encryption complete.\n")); } else { /* * Plaintext PDU (not encrypted) */ if (*parms->wholeMsgLen < parms->scopedPduLen) { DEBUGMSGTL(("ksm", "Not enough room for plaintext PDU.\n")); retval = SNMPERR_TOO_LONG; goto error; } } /* * Start encoding the msgSecurityParameters * * For now, use 0 for the response hint */ DEBUGMSGTL(("ksm", "KSM: scopedPdu added to payload\n")); seq_offset = *offset; rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen, offset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_INTEGER), (long *) &zero, sizeof(zero)); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen, offset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), (u_char *) outdata.data, outdata.length); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm AP_REQ failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } /* * Now, we need to pick the "right" checksum algorithm. For old * crypto, just pick CKSUMTYPE_RSA_MD5_DES; for new crypto, pick * one of the "approved" ones. */ #ifdef MIT_NEW_CRYPTO retcode = krb5_c_keyed_checksum_types(kcontext, subkey->enctype, &numcksumtypes, &cksumtype_array); if (retcode) { DEBUGMSGTL(("ksm", "Unable to find appropriate keyed checksum: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } if (numcksumtypes <= 0) { DEBUGMSGTL(("ksm", "We received a list of zero cksumtypes for this " "enctype (%d)\n", subkey->enctype)); snmp_set_detail("No valid checksum type for this encryption type"); retval = SNMPERR_KRB5; goto error; } /* * It's not clear to me from the API which checksum you're supposed * to support, so I'm taking a guess at the first one */ cksumtype = cksumtype_array[0]; krb5_free_cksumtypes(kcontext, cksumtype_array); DEBUGMSGTL(("ksm", "KSM: Choosing checksum type of %d (subkey type " "of %d)\n", cksumtype, subkey->enctype)); retcode = krb5_c_checksum_length(kcontext, cksumtype, &blocksize); if (retcode) { DEBUGMSGTL(("ksm", "Unable to determine checksum length: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } pdu_checksum.length = blocksize; #else /* MIT_NEW_CRYPTO */ if (ksm_state) cksumtype = ksm_state->cksumtype; else cksumtype = CKSUMTYPE_RSA_MD5_DES; 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; } pdu_checksum.length = krb5_checksum_size(kcontext, cksumtype); pdu_checksum.checksum_type = cksumtype; #endif /* MIT_NEW_CRYPTO */ /* * Note that here, we're just leaving blank space for the checksum; * we remember where that is, and we'll fill it in later. */ *offset += pdu_checksum.length; memset(*wholeMsg + *parms->wholeMsgLen - *offset, 0, pdu_checksum.length); cksum_pointer = *wholeMsg + *parms->wholeMsgLen - *offset; rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen, parms->wholeMsgOffset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), pdu_checksum.length); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen, parms->wholeMsgOffset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), (long *) &cksumtype, sizeof(cksumtype)); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen, parms->wholeMsgOffset, 1, (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), *offset - seq_offset); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen, parms->wholeMsgOffset, 1, (u_char) (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), *offset - seq_offset); if (rc == 0) { DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } DEBUGMSGTL(("ksm", "KSM: Security parameter encoding completed\n")); /* * We're done with the KSM security parameters - now we do the global * header and wrap up the whole PDU. */ if (*parms->wholeMsgLen < parms->globalDataLen) { DEBUGMSGTL(("ksm", "Building global data failed.\n")); retval = SNMPERR_TOO_LONG; goto error; } *offset += parms->globalDataLen; memcpy(*wholeMsg + *parms->wholeMsgLen - *offset, parms->globalData, parms->globalDataLen); rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen, offset, 1, (u_char) (ASN_SEQUENCE | ASN_CONSTRUCTOR), *offset); if (rc == 0) { DEBUGMSGTL(("ksm", "Building master packet sequence.\n")); retval = SNMPERR_TOO_LONG; goto error; } DEBUGMSGTL(("ksm", "KSM: PDU master packet encoding complete.\n")); /* * Now we need to checksum the entire PDU (since it's built). */ pdu_checksum.contents = malloc(pdu_checksum.length); if (!pdu_checksum.contents) { DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum\n", pdu_checksum.length)); retval = SNMPERR_MALLOC; goto error; } /* * If we didn't encrypt the packet, we haven't yet got the subkey. * Get that now. */ if (!subkey) { if (ksm_state) retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey); else retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_getlocalsubkey failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } } #ifdef MIT_NEW_CRYPTO input.data = (char *) (*wholeMsg + *parms->wholeMsgLen - *offset); input.length = *offset; retcode = krb5_c_make_checksum(kcontext, cksumtype, subkey, KSM_KEY_USAGE_CHECKSUM, &input, &pdu_checksum); #else /* MIT_NEW_CRYPTO */ retcode = krb5_calculate_checksum(kcontext, cksumtype, *wholeMsg + *parms->wholeMsgLen - *offset, *offset, (krb5_pointer) subkey->contents, subkey->length, &pdu_checksum); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "Calculate checksum failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } DEBUGMSGTL(("ksm", "KSM: Checksum calculation complete.\n")); memcpy(cksum_pointer, pdu_checksum.contents, pdu_checksum.length); DEBUGMSGTL(("ksm", "KSM: Writing checksum of %d bytes at offset %d\n", pdu_checksum.length, cksum_pointer - (*wholeMsg + 1))); DEBUGMSGTL(("ksm", "KSM: Checksum:")); for (i = 0; i < pdu_checksum.length; i++) DEBUGMSG(("ksm", " %02x", (unsigned int) pdu_checksum.contents[i])); DEBUGMSG(("ksm", "\n")); /* * If we're _not_ called as part of a response (null ksm_state), * then save the auth_context for later using our cache routines. */ if (!ksm_state) { if ((retval = ksm_insert_cache(parms->pdu->msgid, auth_context, (u_char *) parms->secName, parms->secNameLen)) != SNMPERR_SUCCESS) goto error; auth_context = NULL; } DEBUGMSGTL(("ksm", "KSM processing complete!\n")); error: if (pdu_checksum.contents) #ifdef MIT_NEW_CRYPTO krb5_free_checksum_contents(kcontext, &pdu_checksum); #else /* MIT_NEW_CRYPTO */ free(pdu_checksum.contents); #endif /* MIT_NEW_CRYPTO */ if (ivector.data) free(ivector.data); if (subkey) krb5_free_keyblock(kcontext, subkey); if (encrypted_data) free(encrypted_data); if (cc) krb5_cc_close(kcontext, cc); if (auth_context && !ksm_state) krb5_auth_con_free(kcontext, auth_context); return retval; }
/* VARARGS */ int main(int argc, char **argv) { int c, rem; char *cmd, *cp, **ap, buf[RSH_BUFSIZ], **argv0, *args, *args_no_x; char *host = NULL, *user = NULL; int cc; boolean_t asrsh = B_FALSE; struct passwd *pwd; boolean_t readfrom_rem; boolean_t readfrom_rfd2; int one = 1; int omask; boolean_t nflag = B_FALSE; char *krb_realm = NULL; krb5_flags authopts; krb5_error_code status; enum kcmd_proto kcmd_proto = KCMD_NEW_PROTOCOL; uid_t uid = getuid(); c = (argc + 1) * sizeof (char *); if ((argv0 = malloc(c)) == NULL) { perror("malloc"); return (EXIT_FAILURE); } (void) memcpy(argv0, argv, c); (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); /* * Determine command name used to invoke to rlogin(1). Users can * create links named by a host pointing to the binary and type * "hostname" to log into that host afterwards. */ cmd = strrchr(argv[0], '/'); cmd = (cmd != NULL) ? (cmd + 1) : argv[0]; /* * Add "remsh" as an alias for "rsh" (System III, V networking * add-ons often used this name for the remote shell since rsh * was already taken for the restricted shell). Note that this * usurps the ability to use "remsh" as the name of a host (by * symlinking it to rsh), so we go one step farther: if the * file "/usr/bin/remsh" does not exist, we behave as if "remsh" * is a host name. If it does exist, we accept "remsh" as an * "rsh" alias. */ if (strcmp(cmd, "remsh") == 0) { struct stat sb; if (stat("/usr/bin/remsh", &sb) < 0) host = cmd; } else if (strcmp(cmd, "rsh") != 0) { host = cmd; } /* Handle legacy synopsis "rsh hostname options [command]". */ if (host == NULL) { if (argc < 2) usage(); if (*argv[1] != '-') { host = argv[1]; argc--; argv[1] = argv[0]; argv++; asrsh = B_TRUE; } } while ((c = getopt(argc, argv, DEBUGOPTSTRING "8AFKLP:ade:fk:l:nwx")) != -1) { switch (c) { #ifdef DEBUG case 'D': portnumber = htons(atoi(optarg)); krb5auth_flag++; break; #endif /* DEBUG */ case 'F': if (fflag) usage_forward(); Fflag = 1; krb5auth_flag++; fwdable_done = B_TRUE; break; case 'f': if (Fflag) usage_forward(); fflag = 1; krb5auth_flag++; fwd_done = B_TRUE; break; case 'P': if (strcmp(optarg, "N") == 0) kcmd_proto = KCMD_NEW_PROTOCOL; else if (strcmp(optarg, "O") == 0) kcmd_proto = KCMD_OLD_PROTOCOL; else die(gettext("rsh: Only -PN or -PO " "allowed.\n")); if (rcmdoption_done) die(gettext("rsh: Only one of -PN and -PO " "allowed.\n")); rcmdoption_done = B_TRUE; krb5auth_flag++; break; case 'a': krb5auth_flag++; break; case 'K': no_krb5auth_flag++; break; case 'd': options |= SO_DEBUG; break; case 'k': krb_realm = optarg; krb5auth_flag++; break; case 'l': user = optarg; break; case 'n': if (!nflag) { if (close(STDIN_FILENO) < 0) { perror("close"); return (EXIT_FAILURE); } /* * "STDION_FILENO" defined to 0 by POSIX * and hence the lowest file descriptor. * So the open(2) below is guaranteed to * reopen it because we closed it above. */ if (open("/dev/null", O_RDONLY) < 0) { perror("open"); return (EXIT_FAILURE); } nflag = B_TRUE; } break; case 'x': encrypt_flag = 1; krb5auth_flag++; encrypt_done = B_TRUE; break; /* * Ignore the -L, -w, -e and -8 flags to allow aliases with * rlogin to work. Actually rlogin(1) doesn't understand * -w either but because "rsh -w hostname command" used * to work we still accept it. */ case '8': case 'L': case 'e': case 'w': /* * On the lines of the -L, -w, -e and -8 options above, we * ignore the -A option too, in order to allow aliases with * rlogin to work. * * Mind you !, the -a option to trigger Kerberos authentication * in rsh, has a totally different usage in rlogin, its the * -A option (in rlogin) which needs to be used to talk * Kerberos. */ case 'A': break; default: usage(); } } argc -= optind; argv += optind; if (host == NULL) { if (argc == 0) usage(); argc--; host = *argv++; asrsh = B_TRUE; } if (argc == 0) { (void) setreuid(uid, uid); if (nflag) usage(); if (asrsh) *argv0 = "rlogin"; (void) execv(rlogin_path, argv0); perror(rlogin_path); (void) fprintf(stderr, gettext("No local rlogin " "program found.\n")); return (EXIT_FAILURE); } if (__init_suid_priv(0, PRIV_NET_PRIVADDR, NULL) == -1) { (void) fprintf(stderr, gettext("Insufficient privileges, " "rsh must be set-uid root\n")); return (EXIT_FAILURE); } pwd = getpwuid(uid); if (pwd == NULL) { (void) fprintf(stderr, gettext("who are you?\n")); return (EXIT_FAILURE); } if (user == NULL) user = pwd->pw_name; /* * if the user disables krb5 on the cmdline (-K), then skip * all krb5 setup. * * if the user does not disable krb5 or enable krb5 on the * cmdline, check krb5.conf to see if it should be enabled. */ if (no_krb5auth_flag) { krb5auth_flag = 0; Fflag = fflag = encrypt_flag = 0; } else if (!krb5auth_flag) { /* is autologin set in krb5.conf? */ status = krb5_init_context(&bsd_context); /* don't sweat failure here */ if (!status) { /* * note that the call to profile_get_options_boolean * with autologin_option can affect value of * krb5auth_flag */ (void) profile_get_options_boolean(bsd_context->profile, appdef, autologin_option); } } if (krb5auth_flag) { if (!bsd_context) { status = krb5_init_context(&bsd_context); if (status) { com_err("rsh", status, "while initializing krb5"); return (EXIT_FAILURE); } } /* * Get our local realm to look up local realm options. */ status = krb5_get_default_realm(bsd_context, &realmdef[1]); if (status) { com_err("rsh", status, gettext("while getting default realm")); return (EXIT_FAILURE); } /* * Check the realms section in krb5.conf for encryption, * forward & forwardable info */ profile_get_options_boolean(bsd_context->profile, realmdef, option); /* * Check the appdefaults section */ profile_get_options_boolean(bsd_context->profile, appdef, option); profile_get_options_string(bsd_context->profile, appdef, rcmdversion); /* * Set the *_flag variables, if the corresponding *_done are * set to 1, because we dont want the config file values * overriding the command line options. */ if (encrypt_done) encrypt_flag = 1; if (fwd_done) { fflag = 1; Fflag = 0; } else if (fwdable_done) { Fflag = 1; fflag = 0; } if (!rcmdoption_done && (rcmdproto != NULL)) { if (strncmp(rcmdproto, "rcmdv2", 6) == 0) { kcmd_proto = KCMD_NEW_PROTOCOL; } else if (strncmp(rcmdproto, "rcmdv1", 6) == 0) { kcmd_proto = KCMD_OLD_PROTOCOL; } else { (void) fprintf(stderr, gettext("Unrecognized " "KCMD protocol (%s)"), rcmdproto); return (EXIT_FAILURE); } } if (encrypt_flag && (!krb5_privacy_allowed())) { (void) fprintf(stderr, gettext("rsh: Encryption not " "supported.\n")); return (EXIT_FAILURE); } } /* * Connect with the service (shell/kshell) on the daemon side */ if (portnumber == 0) { while (!init_service(krb5auth_flag)) { /* * Connecting to the 'kshell' service failed, * fallback to normal rsh; Reset all KRB5 flags * and connect to 'shell' service on the server */ krb5auth_flag = 0; encrypt_flag = fflag = Fflag = 0; } } cc = encrypt_flag ? strlen(dash_x) : 0; for (ap = argv; *ap != NULL; ap++) cc += strlen(*ap) + 1; cp = args = malloc(cc); if (cp == NULL) perror("malloc"); if (encrypt_flag) { int length; length = strlcpy(args, dash_x, cc); cp += length; cc -= length; } args_no_x = args; for (ap = argv; *ap != NULL; ap++) { int length; length = strlcpy(cp, *ap, cc); assert(length < cc); cp += length; cc -= length; if (ap[1] != NULL) { *cp++ = ' '; cc--; } } if (krb5auth_flag) { authopts = AP_OPTS_MUTUAL_REQUIRED; /* * Piggy-back forwarding flags on top of authopts; * they will be reset in kcmd */ if (fflag || Fflag) authopts |= OPTS_FORWARD_CREDS; if (Fflag) authopts |= OPTS_FORWARDABLE_CREDS; status = kcmd(&rem, &host, portnumber, pwd->pw_name, user, args, &rfd2, "host", krb_realm, bsd_context, &auth_context, &cred, NULL, /* No need for sequence number */ NULL, /* No need for server seq # */ authopts, 1, /* Always set anyport */ &kcmd_proto); if (status != 0) { /* * If new protocol requested, we dont fallback to * less secure ones. */ if (kcmd_proto == KCMD_NEW_PROTOCOL) { (void) fprintf(stderr, gettext("rsh: kcmdv2 " "to host %s failed - %s\n" "Fallback to normal rsh denied."), host, error_message(status)); return (EXIT_FAILURE); } /* check NO_TKT_FILE or equivalent... */ if (status != -1) { (void) fprintf(stderr, gettext("rsh: kcmd to host %s failed - %s\n" "trying normal rsh...\n\n"), host, error_message(status)); } else { (void) fprintf(stderr, gettext("trying normal rsh...\n")); } /* * kcmd() failed, so we now fallback to normal rsh, * after resetting the KRB5 flags and the 'args' array */ krb5auth_flag = 0; encrypt_flag = fflag = Fflag = 0; args = args_no_x; (void) init_service(B_FALSE); } else { /* * Set up buffers for desread and deswrite. */ desinbuf.data = des_inbuf; desoutbuf.data = des_outbuf; desinbuf.length = sizeof (des_inbuf); desoutbuf.length = sizeof (des_outbuf); session_key = &cred->keyblock; if (kcmd_proto == KCMD_NEW_PROTOCOL) { status = krb5_auth_con_getlocalsubkey( bsd_context, auth_context, &session_key); if (status) { com_err("rsh", status, "determining subkey for session"); return (EXIT_FAILURE); } if (session_key == NULL) { com_err("rsh", 0, "no subkey " "negotiated for connection"); return (EXIT_FAILURE); } } eblock.crypto_entry = session_key->enctype; eblock.key = (krb5_keyblock *)session_key; init_encrypt(encrypt_flag, bsd_context, kcmd_proto, &desinbuf, &desoutbuf, CLIENT, &eblock); if (encrypt_flag) { char *s = gettext("This rsh session is using " "encryption for all data transmissions."); (void) write(STDERR_FILENO, s, strlen(s)); (void) write(STDERR_FILENO, "\r\n", 2); } } } /* * Don't merge this with the "if" statement above because * "krb5auth_flag" might be set to false inside it. */ if (!krb5auth_flag) { rem = rcmd_af(&host, portnumber, pwd->pw_name, user, args, &rfd2, AF_INET6); if (rem < 0) return (EXIT_FAILURE); } __priv_relinquish(); if (rfd2 < 0) { (void) fprintf(stderr, gettext("rsh: can't establish " "stderr\n")); return (EXIT_FAILURE); } if (options & SO_DEBUG) { if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, (char *)&one, sizeof (one)) < 0) perror("rsh: setsockopt (stdin)"); if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, (char *)&one, sizeof (one)) < 0) perror("rsh: setsockopt (stderr)"); } omask = sigblock(mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM)); if (sigdisp(SIGINT) != SIG_IGN) (void) sigset(SIGINT, sendsig); if (sigdisp(SIGQUIT) != SIG_IGN) (void) sigset(SIGQUIT, sendsig); if (sigdisp(SIGTERM) != SIG_IGN) (void) sigset(SIGTERM, sendsig); if (nflag) { (void) shutdown(rem, SHUT_WR); } else { child_pid = fork(); if (child_pid < 0) { perror("rsh: fork"); return (EXIT_FAILURE); } if (!encrypt_flag) { (void) ioctl(rfd2, FIONBIO, &one); (void) ioctl(rem, FIONBIO, &one); } if (child_pid == 0) { /* Child */ fd_set remset; char *bp; int wc; (void) close(rfd2); reread: errno = 0; cc = read(0, buf, sizeof (buf)); if (cc <= 0) goto done; bp = buf; rewrite: FD_ZERO(&remset); FD_SET(rem, &remset); if (select(rem + 1, NULL, &remset, NULL, NULL) < 0) { if (errno != EINTR) { perror("rsh: select"); return (EXIT_FAILURE); } goto rewrite; } if (!FD_ISSET(rem, &remset)) goto rewrite; writeiv = B_FALSE; wc = desrshwrite(rem, bp, cc); if (wc < 0) { if (errno == EWOULDBLOCK) goto rewrite; goto done; } cc -= wc; bp += wc; if (cc == 0) goto reread; goto rewrite; done: (void) shutdown(rem, SHUT_WR); return (EXIT_SUCCESS); } } #define MAX(a, b) (((a) > (b)) ? (a) : (b)) sigsetmask(omask); readfrom_rem = B_TRUE; readfrom_rfd2 = B_TRUE; (void) sigset(SIGPIPE, sigpipehandler); do { fd_set readyset; FD_ZERO(&readyset); if (readfrom_rem) FD_SET(rem, &readyset); if (readfrom_rfd2) FD_SET(rfd2, &readyset); if (select(MAX(rem, rfd2) + 1, &readyset, NULL, NULL, NULL) < 0) { if (errno != EINTR) { perror("rsh: select"); return (EXIT_FAILURE); } continue; } if (FD_ISSET(rfd2, &readyset)) { errno = 0; readiv = B_TRUE; cc = desrshread(rfd2, buf, sizeof (buf)); if (cc <= 0) { if (errno != EWOULDBLOCK) readfrom_rfd2 = B_FALSE; } else { (void) write(STDERR_FILENO, buf, cc); } } if (FD_ISSET(rem, &readyset)) { errno = 0; readiv = B_FALSE; cc = desrshread(rem, buf, sizeof (buf)); if (cc <= 0) { if (errno != EWOULDBLOCK) readfrom_rem = B_FALSE; } else (void) write(STDOUT_FILENO, buf, cc); } } while (readfrom_rem || readfrom_rfd2); if (!nflag) (void) kill(child_pid, SIGKILL); return (EXIT_SUCCESS); }
static int send_krb5_auth(int s, struct sockaddr *thisaddr, struct sockaddr *thataddr, const char *hostname, const char *remote_user, const char *local_user, size_t cmd_len, const char *cmd) { krb5_principal server; krb5_data cksum_data; int status; size_t len; krb5_auth_context auth_context = NULL; const char *protocol_string = NULL; krb5_flags ap_opts; char *str; status = krb5_sname_to_principal(context, hostname, "host", KRB5_NT_SRV_HST, &server); if (status) { warnx ("%s: %s", hostname, krb5_get_err_text(context, status)); return 1; } if(do_encrypt == -1) { krb5_appdefault_boolean(context, NULL, krb5_principal_get_realm(context, server), "encrypt", FALSE, &do_encrypt); } cksum_data.length = asprintf (&str, "%u:%s%s%s", ntohs(socket_get_port(thataddr)), do_encrypt ? "-x " : "", cmd, remote_user); if (str == NULL) { warnx ("%s: failed to allocate command", hostname); return 1; } cksum_data.data = str; ap_opts = 0; if(do_encrypt) ap_opts |= AP_OPTS_MUTUAL_REQUIRED; switch(protocol_version) { case 2: ap_opts |= AP_OPTS_USE_SUBKEY; protocol_string = KCMD_NEW_VERSION; break; case 1: protocol_string = KCMD_OLD_VERSION; key_usage = KRB5_KU_OTHER_ENCRYPTED; break; default: abort(); } status = krb5_sendauth (context, &auth_context, &s, protocol_string, NULL, server, ap_opts, &cksum_data, NULL, NULL, NULL, NULL, NULL); /* do this while we have a principal */ if(do_forward == -1 || do_forwardable == -1) { krb5_const_realm realm = krb5_principal_get_realm(context, server); if (do_forwardable == -1) krb5_appdefault_boolean(context, NULL, realm, "forwardable", FALSE, &do_forwardable); if (do_forward == -1) krb5_appdefault_boolean(context, NULL, realm, "forward", FALSE, &do_forward); } krb5_free_principal(context, server); krb5_data_free(&cksum_data); if (status) { if(status == KRB5_SENDAUTH_REJECTED && protocol_version == 2 && protocol_version_str == NULL) sendauth_version_error = 1; else krb5_warn(context, status, "%s", hostname); return 1; } status = krb5_auth_con_getlocalsubkey (context, auth_context, &keyblock); if(keyblock == NULL) status = krb5_auth_con_getkey (context, auth_context, &keyblock); if (status) { warnx ("krb5_auth_con_getkey: %s", krb5_get_err_text(context, status)); return 1; } status = krb5_auth_con_setaddrs_from_fd (context, auth_context, &s); if (status) { warnx("krb5_auth_con_setaddrs_from_fd: %s", krb5_get_err_text(context, status)); return(1); } status = krb5_crypto_init(context, keyblock, 0, &crypto); if(status) { warnx ("krb5_crypto_init: %s", krb5_get_err_text(context, status)); return 1; } len = strlen(remote_user) + 1; if (net_write (s, remote_user, len) != len) { warn ("write"); return 1; } if (do_encrypt && net_write (s, "-x ", 3) != 3) { warn ("write"); return 1; } if (net_write (s, cmd, cmd_len) != cmd_len) { warn ("write"); return 1; } if (do_unique_tkfile) { if (net_write (s, tkfile, strlen(tkfile)) != strlen(tkfile)) { warn ("write"); return 1; } } len = strlen(local_user) + 1; if (net_write (s, local_user, len) != len) { warn ("write"); return 1; } if (!do_forward || krb5_forward_cred (auth_context, s, hostname, do_forwardable)) { /* Empty forwarding info */ u_char zero[4] = {0, 0, 0, 0}; write (s, &zero, 4); } krb5_auth_con_free (context, auth_context); return 0; }
/* authentication, client side */ int kerberos_auth (krb5_context *ctx, int verbose, char **cname, const char *sname, int sock, char *cmd, unsigned short port, krb5_keyblock **key, const char *realm) { int rc; char *out, *p; size_t outlen; int krb5len, msglen; char *tmpserver; char auth; /* KERBEROS 5 SENDAUTH MESSAGE */ char krb5sendauth[] = "KRB5_SENDAUTH_V1.0"; /* PROTOCOL VERSION */ char krb5sendclient[] = "KCMDV0.2"; /* to store error msg sent by server */ char errormsg[101]; char cksumdata[101]; krb5_data cksum_data; krb5_principal server; krb5_auth_context auth_ctx = NULL; krb5_flags authopts = AP_OPTS_USE_SUBKEY; if (krb5_sname_to_principal (*ctx, sname, "host", KRB5_NT_SRV_HST, &server)) return (-1); /* If realm is null, look up from table */ if (realm == NULL || realm[0] == '\0') # ifdef KRB5_GENERAL__ /* MIT */ realm = (char *) krb5_princ_realm (*ctx, server); # else /* Heimdal */ realm = krb5_principal_get_realm (*ctx, server); # endif /* size of KRB5 auth message */ krb5len = strlen (krb5sendauth) + 1; msglen = htonl (krb5len); write (sock, &msglen, sizeof (int)); /* KRB5 authentication message */ write (sock, krb5sendauth, krb5len); /* size of client message */ krb5len = strlen (krb5sendclient) + 1; msglen = htonl (krb5len); write (sock, &msglen, sizeof (int)); /* KRB5 client message */ write (sock, krb5sendclient, krb5len); /* get answer from server 0 = ok, 1 = error with message */ read (sock, &auth, 1); if (auth) { ssize_t n; errormsg[0] = '\0'; n = read (sock, errormsg, sizeof (errormsg) - 1); if (n >= 0 && n < (ssize_t) sizeof (errormsg)) errormsg[n] = '\0'; else errormsg[sizeof (errormsg) -1] = '\0'; fprintf (stderr, "Error during server authentication : %s\n", errormsg); return -1; } if (verbose) { printf ("Client: %s\n", *cname); printf ("Server: %s\n", sname); } /* Get a ticket for the server. */ tmpserver = malloc (strlen (SERVICE) + strlen (sname) + 2); if (!tmpserver) { perror ("kerberos_auth()"); return -1; } p = strchr (sname, '/'); if (p && (p != sname)) strcpy (tmpserver, sname); /* Non-empty prefix. */ else sprintf (tmpserver, "%s/%s", SERVICE, sname + (p ? 1 : 0)); /* Retrieve realm assigned to this server as per configuration, * unless an explicit domain was passed in the call. */ if (!realm) { if (!p) p = (char *) sname; else if (*p == '/') ++p; } /* checksum = port: terminal name */ cksum_data.length = snprintf (cksumdata, sizeof (cksumdata) - 1, "%u:%s%s", ntohs (port), cmd, *cname); if (strncmp (cmd, "-x ", 3) == 0) authopts |= AP_OPTS_MUTUAL_REQUIRED; cksum_data.data = cksumdata; rc = krb5_sendauth (*ctx, &auth_ctx, &sock, "KCMDV0.2", NULL, server, authopts, &cksum_data, NULL, NULL, NULL, NULL, NULL); if (rc == KRB5_SENDAUTH_REJECTED) { fprintf (stderr, "server rejected authentication"); return rc; } krb5_free_principal (*ctx, server); # if 0 krb5_data_free (&cksum_data); # endif rc = krb5_auth_con_getlocalsubkey (*ctx, auth_ctx, key); /* send size of AP-REQ to the server */ msglen = outlen; msglen = htonl (msglen); write (sock, (char *) &msglen, sizeof (int)); /* send AP-REQ to the server */ write (sock, out, outlen); /* read response from server - what ? */ read (sock, &rc, sizeof (rc)); if (rc) return -1 /* SHISHI_APREP_VERIFY_FAILED */; /* For mutual authentication, wait for server reply. */ /* We are now authenticated. */ if (verbose) printf ("User authenticated.\n"); return 0; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL _krb5_build_authenticator (krb5_context context, krb5_auth_context auth_context, krb5_enctype enctype, krb5_creds *cred, Checksum *cksum, krb5_data *result, krb5_key_usage usage) { Authenticator auth; u_char *buf = NULL; size_t buf_size; size_t len = 0; krb5_error_code ret; krb5_crypto crypto; memset(&auth, 0, sizeof(auth)); auth.authenticator_vno = 5; copy_Realm(&cred->client->realm, &auth.crealm); copy_PrincipalName(&cred->client->name, &auth.cname); krb5_us_timeofday (context, &auth.ctime, &auth.cusec); ret = krb5_auth_con_getlocalsubkey(context, auth_context, &auth.subkey); if(ret) goto fail; if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { if(auth_context->local_seqnumber == 0) krb5_generate_seq_number (context, &cred->session, &auth_context->local_seqnumber); ALLOC(auth.seq_number, 1); if(auth.seq_number == NULL) { ret = ENOMEM; goto fail; } *auth.seq_number = auth_context->local_seqnumber; } else auth.seq_number = NULL; auth.authorization_data = NULL; if (cksum) { ALLOC(auth.cksum, 1); if (auth.cksum == NULL) { ret = ENOMEM; goto fail; } ret = copy_Checksum(cksum, auth.cksum); if (ret) goto fail; if (auth.cksum->cksumtype == CKSUMTYPE_GSSAPI) { /* * This is not GSS-API specific, we only enable it for * GSS for now */ ret = make_etypelist(context, &auth.authorization_data); if (ret) goto fail; } } /* XXX - Copy more to auth_context? */ auth_context->authenticator->ctime = auth.ctime; auth_context->authenticator->cusec = auth.cusec; ASN1_MALLOC_ENCODE(Authenticator, buf, buf_size, &auth, &len, ret); if (ret) goto fail; if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_crypto_init(context, &cred->session, enctype, &crypto); if (ret) goto fail; ret = krb5_encrypt (context, crypto, usage /* KRB5_KU_AP_REQ_AUTH */, buf, len, result); krb5_crypto_destroy(context, crypto); if (ret) goto fail; fail: free_Authenticator (&auth); free (buf); return ret; }
krb5_error_code _krb5_init_tgs_req(krb5_context context, krb5_ccache ccache, krb5_addresses *addresses, krb5_kdc_flags flags, krb5_const_principal impersonate_principal, Ticket *second_ticket, krb5_creds *in_creds, krb5_creds *krbtgt, unsigned nonce, METHOD_DATA *padata, krb5_keyblock **subkey, TGS_REQ *t) { krb5_auth_context ac = NULL; krb5_error_code ret = 0; /* inherit the forwardable/proxyable flags from the krbtgt */ flags.b.forwardable = krbtgt->flags.b.forwardable; flags.b.proxiable = krbtgt->flags.b.proxiable; if (ccache->ops->tgt_req) { KERB_TGS_REQ_OUT out; KERB_TGS_REQ_IN in; memset(&in, 0, sizeof(in)); memset(&out, 0, sizeof(out)); ret = ccache->ops->tgt_req(context, ccache, &in, &out); if (ret) return ret; free_KERB_TGS_REQ_OUT(&out); return 0; } memset(t, 0, sizeof(*t)); if (impersonate_principal) { krb5_crypto crypto; PA_S4U2Self self; krb5_data data; void *buf; size_t size, len; self.name = impersonate_principal->name; self.realm = impersonate_principal->realm; self.auth = rk_UNCONST("Kerberos"); ret = _krb5_s4u2self_to_checksumdata(context, &self, &data); if (ret) goto fail; ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto); if (ret) { krb5_data_free(&data); goto fail; } ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0, data.data, data.length, &self.cksum); krb5_crypto_destroy(context, crypto); krb5_data_free(&data); if (ret) goto fail; ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret); free_Checksum(&self.cksum); if (ret) goto fail; if (len != size) krb5_abortx(context, "internal asn1 error"); ret = krb5_padata_add(context, padata, KRB5_PADATA_FOR_USER, buf, len); if (ret) goto fail; } t->pvno = 5; t->msg_type = krb_tgs_req; if (in_creds->session.keytype) { ALLOC_SEQ(&t->req_body.etype, 1); if(t->req_body.etype.val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } t->req_body.etype.val[0] = in_creds->session.keytype; } else { ret = _krb5_init_etype(context, KRB5_PDU_TGS_REQUEST, &t->req_body.etype.len, &t->req_body.etype.val, NULL); } if (ret) goto fail; t->req_body.addresses = addresses; t->req_body.kdc_options = flags.b; ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm); if (ret) goto fail; ALLOC(t->req_body.sname, 1); if (t->req_body.sname == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } /* some versions of some code might require that the client be present in TGS-REQs, but this is clearly against the spec */ ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname); if (ret) goto fail; /* req_body.till should be NULL if there is no endtime specified, but old MIT code (like DCE secd) doesn't like that */ ALLOC(t->req_body.till, 1); if(t->req_body.till == NULL){ ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } *t->req_body.till = in_creds->times.endtime; t->req_body.nonce = nonce; if(second_ticket){ ALLOC(t->req_body.additional_tickets, 1); if (t->req_body.additional_tickets == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ALLOC_SEQ(t->req_body.additional_tickets, 1); if (t->req_body.additional_tickets->val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val); if (ret) goto fail; } ALLOC(t->padata, 1); if (t->padata == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ALLOC_SEQ(t->padata, 1 + padata->len); if (t->padata->val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } { size_t i; for (i = 0; i < padata->len; i++) { ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]); if (ret) { krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } } } ret = krb5_auth_con_init(context, &ac); if(ret) goto fail; ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session); if (ret) goto fail; ret = set_auth_data (context, &t->req_body, &in_creds->authdata, ac->local_subkey); if (ret) goto fail; ret = make_pa_tgs_req(context, ac, &t->req_body, &t->padata->val[0], ccache, krbtgt); if(ret) goto fail; ret = krb5_auth_con_getlocalsubkey(context, ac, subkey); if (ret) goto fail; fail: if (ac) krb5_auth_con_free(context, ac); if (ret) { t->req_body.addresses = NULL; free_TGS_REQ (t); } return ret; }
static int k5_auth_send(kstream ks, int how) { krb5_error_code r; krb5_ccache ccache; krb5_creds creds; krb5_creds * new_creds; extern krb5_flags krb5_kdc_default_options; krb5_flags ap_opts; char type_check[2]; krb5_data check_data; int len; #ifdef ENCRYPTION krb5_keyblock *newkey = 0; #endif if (r = krb5_cc_default(k5_context, &ccache)) { com_err(NULL, r, "while authorizing."); return(0); } memset((char *)&creds, 0, sizeof(creds)); if (r = krb5_sname_to_principal(k5_context, szHostName, KRB_SERVICE_NAME, KRB5_NT_SRV_HST, &creds.server)) { com_err(NULL, r, "while authorizing."); return(0); } if (r = krb5_cc_get_principal(k5_context, ccache, &creds.client)) { com_err(NULL, r, "while authorizing."); krb5_free_cred_contents(k5_context, &creds); return(0); } if (szUserName[0] == '\0') { /* Get user name now */ len = krb5_princ_component(k5_context, creds.client, 0)->length; memcpy(szUserName, krb5_princ_component(k5_context, creds.client, 0)->data, len); szUserName[len] = '\0'; } if (r = krb5_get_credentials(k5_context, 0, ccache, &creds, &new_creds)) { com_err(NULL, r, "while authorizing."); krb5_free_cred_contents(k5_context, &creds); return(0); } ap_opts = 0; if ((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ap_opts = AP_OPTS_MUTUAL_REQUIRED; #ifdef ENCRYPTION ap_opts |= AP_OPTS_USE_SUBKEY; #endif if (auth_context) { krb5_auth_con_free(k5_context, auth_context); auth_context = 0; } if ((r = krb5_auth_con_init(k5_context, &auth_context))) { com_err(NULL, r, "while initializing auth context"); return(0); } krb5_auth_con_setflags(k5_context, auth_context, KRB5_AUTH_CONTEXT_RET_TIME); type_check[0] = AUTHTYPE_KERBEROS_V5; type_check[1] = AUTH_WHO_CLIENT| (how & AUTH_HOW_MASK); #ifdef ENCRYPTION type_check[1] |= AUTH_ENCRYPT_ON; #endif check_data.magic = KV5M_DATA; check_data.length = 2; check_data.data = (char *)&type_check; r = krb5_mk_req_extended(k5_context, &auth_context, ap_opts, NULL, new_creds, &auth); #ifdef ENCRYPTION krb5_auth_con_getlocalsubkey(k5_context, auth_context, &newkey); if (session_key) { krb5_free_keyblock(k5_context, session_key); session_key = 0; } if (newkey) { /* * keep the key in our private storage, but don't use it * yet---see kerberos5_reply() below */ if ((newkey->enctype != ENCTYPE_DES_CBC_CRC) && (newkey-> enctype != ENCTYPE_DES_CBC_MD5)) { if ((new_creds->keyblock.enctype == ENCTYPE_DES_CBC_CRC) || (new_creds->keyblock.enctype == ENCTYPE_DES_CBC_MD5)) /* use the session key in credentials instead */ krb5_copy_keyblock(k5_context, &new_creds->keyblock, &session_key); else ; /* What goes here? XXX */ } else { krb5_copy_keyblock(k5_context, newkey, &session_key); } krb5_free_keyblock(k5_context, newkey); } #endif /* ENCRYPTION */ krb5_free_cred_contents(k5_context, &creds); krb5_free_creds(k5_context, new_creds); if (r) { com_err(NULL, r, "while authorizing."); return(0); } return(1); }
static krb5_error_code init_tgs_req (krb5_context context, krb5_ccache ccache, krb5_addresses *addresses, krb5_kdc_flags flags, Ticket *second_ticket, krb5_creds *in_creds, krb5_creds *krbtgt, unsigned nonce, const METHOD_DATA *padata, krb5_keyblock **subkey, TGS_REQ *t) { krb5_auth_context ac = NULL; krb5_error_code ret = 0; memset(t, 0, sizeof(*t)); t->pvno = 5; t->msg_type = krb_tgs_req; if (in_creds->session.keytype) { ALLOC_SEQ(&t->req_body.etype, 1); if(t->req_body.etype.val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } t->req_body.etype.val[0] = in_creds->session.keytype; } else { ret = krb5_init_etype(context, &t->req_body.etype.len, &t->req_body.etype.val, NULL); } if (ret) goto fail; t->req_body.addresses = addresses; t->req_body.kdc_options = flags.b; ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm); if (ret) goto fail; ALLOC(t->req_body.sname, 1); if (t->req_body.sname == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } /* some versions of some code might require that the client be present in TGS-REQs, but this is clearly against the spec */ ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname); if (ret) goto fail; /* req_body.till should be NULL if there is no endtime specified, but old MIT code (like DCE secd) doesn't like that */ ALLOC(t->req_body.till, 1); if(t->req_body.till == NULL){ ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } *t->req_body.till = in_creds->times.endtime; t->req_body.nonce = nonce; if(second_ticket){ ALLOC(t->req_body.additional_tickets, 1); if (t->req_body.additional_tickets == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ALLOC_SEQ(t->req_body.additional_tickets, 1); if (t->req_body.additional_tickets->val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val); if (ret) goto fail; } ALLOC(t->padata, 1); if (t->padata == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } ALLOC_SEQ(t->padata, 1 + padata->len); if (t->padata->val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } { int i; for (i = 0; i < padata->len; i++) { ret = copy_PA_DATA(&padata->val[i], &t->padata->val[i + 1]); if (ret) { krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto fail; } } } ret = krb5_auth_con_init(context, &ac); if(ret) goto fail; ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session); if (ret) goto fail; ret = set_auth_data (context, &t->req_body, &in_creds->authdata, ac->local_subkey); if (ret) goto fail; ret = make_pa_tgs_req(context, ac, &t->req_body, &t->padata->val[0], krbtgt); if(ret) goto fail; ret = krb5_auth_con_getlocalsubkey(context, ac, subkey); if (ret) goto fail; fail: if (ac) krb5_auth_con_free(context, ac); if (ret) { t->req_body.addresses = NULL; free_TGS_REQ (t); } return ret; }
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; }