//---------------------------------------------------------------------- static void make_new_name(ushort name, ushort subnam, uchar mode, unsigned ip) { char str[MAXNAMELEN]; init_output_buffer(str, sizeof(str)); if ( fmtString(name, sizeof(str)-2, fmt_fullname) ) { trunc: trunc_name(ip, mode & 4); } else if ( (char)mode > 0 ) { register char *p = get_output_ptr(); if ( p >= &str[sizeof(str)-3] ) goto trunc; *p++ = '.'; set_output_ptr(p); if ( fmtString(subnam, &str[sizeof(str)-2] - p, fmt_name) ) goto trunc; } term_output_buffer(); do_name_anyway(ip, convert_clsname(str)); hide_name(ip); }
/* Callback from GSSRPC for garbled/forged/replayed/etc messages. */ static void log_badverf(gss_name_t client_name, gss_name_t server_name, struct svc_req *rqst, struct rpc_msg *msg, char *data) { static const struct { rpcproc_t proc; const char *proc_name; } proc_names[] = { {1, "CREATE_PRINCIPAL"}, {2, "DELETE_PRINCIPAL"}, {3, "MODIFY_PRINCIPAL"}, {4, "RENAME_PRINCIPAL"}, {5, "GET_PRINCIPAL"}, {6, "CHPASS_PRINCIPAL"}, {7, "CHRAND_PRINCIPAL"}, {8, "CREATE_POLICY"}, {9, "DELETE_POLICY"}, {10, "MODIFY_POLICY"}, {11, "GET_POLICY"}, {12, "GET_PRIVS"}, {13, "INIT"}, {14, "GET_PRINCS"}, {15, "GET_POLS"}, {16, "SETKEY_PRINCIPAL"}, {17, "SETV4KEY_PRINCIPAL"}, {18, "CREATE_PRINCIPAL3"}, {19, "CHPASS_PRINCIPAL3"}, {20, "CHRAND_PRINCIPAL3"}, {21, "SETKEY_PRINCIPAL3"}, {22, "PURGEKEYS"}, {23, "GET_STRINGS"}, {24, "SET_STRING"} }; OM_uint32 minor; gss_buffer_desc client, server; gss_OID gss_type; const char *a; rpcproc_t proc; unsigned int i; const char *procname; size_t clen, slen; char *cdots, *sdots; client.length = 0; client.value = NULL; server.length = 0; server.value = NULL; (void)gss_display_name(&minor, client_name, &client, &gss_type); (void)gss_display_name(&minor, server_name, &server, &gss_type); if (client.value == NULL) { client.value = "(null)"; clen = sizeof("(null)") - 1; } else { clen = client.length; } trunc_name(&clen, &cdots); if (server.value == NULL) { server.value = "(null)"; slen = sizeof("(null)") - 1; } else { slen = server.length; } trunc_name(&slen, &sdots); a = client_addr(rqst->rq_xprt); proc = msg->rm_call.cb_proc; procname = NULL; for (i = 0; i < sizeof(proc_names) / sizeof(*proc_names); i++) { if (proc_names[i].proc == proc) { procname = proc_names[i].proc_name; break; } } if (procname != NULL) { krb5_klog_syslog(LOG_NOTICE, _("WARNING! Forged/garbled request: %s, claimed " "client = %.*s%s, server = %.*s%s, addr = %s"), procname, (int)clen, (char *)client.value, cdots, (int)slen, (char *)server.value, sdots, a); } else { krb5_klog_syslog(LOG_NOTICE, _("WARNING! Forged/garbled request: %d, claimed " "client = %.*s%s, server = %.*s%s, addr = %s"), proc, (int)clen, (char *)client.value, cdots, (int)slen, (char *)server.value, sdots, a); } (void)gss_release_buffer(&minor, &client); (void)gss_release_buffer(&minor, &server); }
static int check_iprop_rpcsec_auth(struct svc_req *rqstp) { /* XXX Since the client can authenticate against any principal in the database, we need to do a sanity check. Only checking for "kiprop" now, but that means theoretically the client could be authenticating to kiprop on some other machine. */ /* Code taken from kadm_rpc_svc.c, tweaked. */ gss_ctx_id_t ctx; krb5_context kctx; OM_uint32 maj_stat, min_stat; gss_name_t name; krb5_principal princ; int ret, success; krb5_data *c1, *c2, *realm; gss_buffer_desc gss_str; kadm5_server_handle_t handle; size_t slen; char *sdots; success = 0; handle = (kadm5_server_handle_t)global_server_handle; if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS) return 0; ctx = rqstp->rq_svccred; maj_stat = gss_inquire_context(&min_stat, ctx, NULL, &name, NULL, NULL, NULL, NULL, NULL); if (maj_stat != GSS_S_COMPLETE) { krb5_klog_syslog(LOG_ERR, "check_rpcsec_auth: " "failed inquire_context, stat=%u", maj_stat); log_badauth(maj_stat, min_stat, &rqstp->rq_xprt->xp_raddr, NULL); goto fail_name; } kctx = handle->context; ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str); if (ret == 0) goto fail_name; slen = gss_str.length; trunc_name(&slen, &sdots); /* * Since we accept with GSS_C_NO_NAME, the client can authenticate * against the entire kdb. Therefore, ensure that the service * name is something reasonable. */ if (krb5_princ_size(kctx, princ) != 2) goto fail_princ; c1 = krb5_princ_component(kctx, princ, 0); c2 = krb5_princ_component(kctx, princ, 1); realm = krb5_princ_realm(kctx, princ); if (strncmp(handle->params.realm, realm->data, realm->length) == 0 && strncmp("kiprop", c1->data, c1->length) == 0) { success = 1; } fail_princ: if (!success) { krb5_klog_syslog(LOG_ERR, "bad service principal %.*s%s", (int) slen, (char *) gss_str.value, sdots); } gss_release_buffer(&min_stat, &gss_str); krb5_free_principal(kctx, princ); fail_name: gss_release_name(&min_stat, &name); return success; }
static krb5_error_code process_chpw_request(krb5_context context, void *server_handle, char *realm, krb5_keytab keytab, const krb5_fulladdr *local_faddr, const krb5_fulladdr *remote_faddr, krb5_data *req, krb5_data *rep) { krb5_error_code ret; char *ptr; unsigned int plen, vno; krb5_data ap_req, ap_rep = empty_data(); krb5_data cipher = empty_data(), clear = empty_data(); krb5_auth_context auth_context = NULL; krb5_principal changepw = NULL; krb5_principal client, target = NULL; krb5_ticket *ticket = NULL; krb5_replay_data replay; krb5_error krberror; int numresult; char strresult[1024]; char *clientstr = NULL, *targetstr = NULL; const char *errmsg = NULL; size_t clen; char *cdots; struct sockaddr_storage ss; socklen_t salen; char addrbuf[100]; krb5_address *addr = remote_faddr->address; *rep = empty_data(); if (req->length < 4) { /* either this, or the server is printing bad messages, or the caller passed in garbage */ ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Request was truncated", sizeof(strresult)); goto bailout; } ptr = req->data; /* verify length */ plen = (*ptr++ & 0xff); plen = (plen<<8) | (*ptr++ & 0xff); if (plen != req->length) { ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Request length was inconsistent", sizeof(strresult)); goto bailout; } /* verify version number */ vno = (*ptr++ & 0xff) ; vno = (vno<<8) | (*ptr++ & 0xff); if (vno != 1 && vno != RFC3244_VERSION) { ret = KRB5KDC_ERR_BAD_PVNO; numresult = KRB5_KPASSWD_BAD_VERSION; snprintf(strresult, sizeof(strresult), "Request contained unknown protocol version number %d", vno); goto bailout; } /* read, check ap-req length */ ap_req.length = (*ptr++ & 0xff); ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff); if (ptr + ap_req.length >= req->data + req->length) { ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Request was truncated in AP-REQ", sizeof(strresult)); goto bailout; } /* verify ap_req */ ap_req.data = ptr; ptr += ap_req.length; ret = krb5_auth_con_init(context, &auth_context); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed initializing auth context", sizeof(strresult)); goto chpwfail; } ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed initializing auth context", sizeof(strresult)); goto chpwfail; } ret = krb5_build_principal(context, &changepw, strlen(realm), realm, "kadmin", "changepw", NULL); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed building kadmin/changepw principal", sizeof(strresult)); goto chpwfail; } ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab, NULL, &ticket); if (ret) { numresult = KRB5_KPASSWD_AUTHERROR; strlcpy(strresult, "Failed reading application request", sizeof(strresult)); goto chpwfail; } /* construct the ap-rep */ ret = krb5_mk_rep(context, auth_context, &ap_rep); if (ret) { numresult = KRB5_KPASSWD_AUTHERROR; strlcpy(strresult, "Failed replying to application request", sizeof(strresult)); goto chpwfail; } /* decrypt the ChangePasswdData */ cipher.length = (req->data + req->length) - ptr; cipher.data = ptr; /* * Don't set a remote address in auth_context before calling krb5_rd_priv, * so that we can work against clients behind a NAT. Reflection attacks * aren't a concern since we use sequence numbers and since our requests * don't look anything like our responses. Also don't set a local address, * since we don't know what interface the request was received on. */ ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed decrypting request", sizeof(strresult)); goto chpwfail; } client = ticket->enc_part2->client; /* decode ChangePasswdData for setpw requests */ if (vno == RFC3244_VERSION) { krb5_data *clear_data; ret = decode_krb5_setpw_req(&clear, &clear_data, &target); if (ret != 0) { numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Failed decoding ChangePasswdData", sizeof(strresult)); goto chpwfail; } zapfree(clear.data, clear.length); clear = *clear_data; free(clear_data); if (target != NULL) { ret = krb5_unparse_name(context, target, &targetstr); if (ret != 0) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed unparsing target name for log", sizeof(strresult)); goto chpwfail; } } } ret = krb5_unparse_name(context, client, &clientstr); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed unparsing client name for log", sizeof(strresult)); goto chpwfail; } /* for cpw, verify that this is an AS_REQ ticket */ if (vno == 1 && (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) { numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED; strlcpy(strresult, "Ticket must be derived from a password", sizeof(strresult)); goto chpwfail; } /* change the password */ ptr = k5memdup0(clear.data, clear.length, &ret); ret = schpw_util_wrapper(server_handle, client, target, (ticket->enc_part2->flags & TKT_FLG_INITIAL) != 0, ptr, NULL, strresult, sizeof(strresult)); if (ret) errmsg = krb5_get_error_message(context, ret); /* zap the password */ zapfree(clear.data, clear.length); zapfree(ptr, clear.length); clear = empty_data(); clen = strlen(clientstr); trunc_name(&clen, &cdots); switch (addr->addrtype) { case ADDRTYPE_INET: { struct sockaddr_in *sin = ss2sin(&ss); sin->sin_family = AF_INET; memcpy(&sin->sin_addr, addr->contents, addr->length); sin->sin_port = htons(remote_faddr->port); salen = sizeof(*sin); break; } case ADDRTYPE_INET6: { struct sockaddr_in6 *sin6 = ss2sin6(&ss); sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, addr->contents, addr->length); sin6->sin6_port = htons(remote_faddr->port); salen = sizeof(*sin6); break; } default: { struct sockaddr *sa = ss2sa(&ss); sa->sa_family = AF_UNSPEC; salen = sizeof(*sa); break; } } if (getnameinfo(ss2sa(&ss), salen, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) != 0) strlcpy(addrbuf, "<unprintable>", sizeof(addrbuf)); if (vno == RFC3244_VERSION) { size_t tlen; char *tdots; const char *targetp; if (target == NULL) { tlen = clen; tdots = cdots; targetp = targetstr; } else { tlen = strlen(targetstr); trunc_name(&tlen, &tdots); targetp = clientstr; } krb5_klog_syslog(LOG_NOTICE, _("setpw request from %s by %.*s%s for " "%.*s%s: %s"), addrbuf, (int) clen, clientstr, cdots, (int) tlen, targetp, tdots, errmsg ? errmsg : "success"); } else { krb5_klog_syslog(LOG_NOTICE, _("chpw request from %s for %.*s%s: %s"), addrbuf, (int) clen, clientstr, cdots, errmsg ? errmsg : "success"); } switch (ret) { case KADM5_AUTH_CHANGEPW: numresult = KRB5_KPASSWD_ACCESSDENIED; break; case KADM5_PASS_Q_TOOSHORT: case KADM5_PASS_REUSE: case KADM5_PASS_Q_CLASS: case KADM5_PASS_Q_DICT: case KADM5_PASS_Q_GENERIC: case KADM5_PASS_TOOSOON: numresult = KRB5_KPASSWD_SOFTERROR; break; case 0: numresult = KRB5_KPASSWD_SUCCESS; strlcpy(strresult, "", sizeof(strresult)); break; default: numresult = KRB5_KPASSWD_HARDERROR; break; } chpwfail: ret = alloc_data(&clear, 2 + strlen(strresult)); if (ret) goto bailout; ptr = clear.data; *ptr++ = (numresult>>8) & 0xff; *ptr++ = numresult & 0xff; memcpy(ptr, strresult, strlen(strresult)); cipher = empty_data(); if (ap_rep.length) { ret = krb5_auth_con_setaddrs(context, auth_context, local_faddr->address, NULL); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed storing client and server internet addresses", sizeof(strresult)); } else { ret = krb5_mk_priv(context, auth_context, &clear, &cipher, &replay); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed encrypting reply", sizeof(strresult)); } } } /* if no KRB-PRIV was constructed, then we need a KRB-ERROR. if this fails, just bail. there's nothing else we can do. */ if (cipher.length == 0) { /* clear out ap_rep now, so that it won't be inserted in the reply */ if (ap_rep.length) { free(ap_rep.data); ap_rep = empty_data(); } krberror.ctime = 0; krberror.cusec = 0; krberror.susec = 0; ret = krb5_timeofday(context, &krberror.stime); if (ret) goto bailout; /* this is really icky. but it's what all the other callers to mk_error do. */ krberror.error = ret; krberror.error -= ERROR_TABLE_BASE_krb5; if (krberror.error < 0 || krberror.error > KRB_ERR_MAX) krberror.error = KRB_ERR_GENERIC; krberror.client = NULL; ret = krb5_build_principal(context, &krberror.server, strlen(realm), realm, "kadmin", "changepw", NULL); if (ret) goto bailout; krberror.text.length = 0; krberror.e_data = clear; ret = krb5_mk_error(context, &krberror, &cipher); krb5_free_principal(context, krberror.server); if (ret) goto bailout; } /* construct the reply */ ret = alloc_data(rep, 6 + ap_rep.length + cipher.length); if (ret) goto bailout; ptr = rep->data; /* length */ *ptr++ = (rep->length>>8) & 0xff; *ptr++ = rep->length & 0xff; /* version == 0x0001 big-endian */ *ptr++ = 0; *ptr++ = 1; /* ap_rep length, big-endian */ *ptr++ = (ap_rep.length>>8) & 0xff; *ptr++ = ap_rep.length & 0xff; /* ap-rep data */ if (ap_rep.length) { memcpy(ptr, ap_rep.data, ap_rep.length); ptr += ap_rep.length; } /* krb-priv or krb-error */ memcpy(ptr, cipher.data, cipher.length); bailout: krb5_auth_con_free(context, auth_context); krb5_free_principal(context, changepw); krb5_free_ticket(context, ticket); free(ap_rep.data); free(clear.data); free(cipher.data); krb5_free_principal(context, target); krb5_free_unparsed_name(context, targetstr); krb5_free_unparsed_name(context, clientstr); krb5_free_error_message(context, errmsg); return ret; }