static krb5_error_code locate_kpasswd(krb5_context context, const krb5_data *realm, struct serverlist *serverlist, int socktype) { krb5_error_code code; code = k5_locate_server(context, realm, serverlist, locate_service_kpasswd, socktype); if (code == KRB5_REALM_CANT_RESOLVE || code == KRB5_REALM_UNKNOWN) { code = k5_locate_server(context, realm, serverlist, locate_service_kadmin, SOCK_STREAM); if (!code) { /* Success with admin_server but now we need to change the port number to use DEFAULT_KPASSWD_PORT and the socktype. */ size_t i; for (i = 0; i < serverlist->nservers; i++) { struct server_entry *s = &serverlist->servers[i]; krb5_ui_2 kpasswd_port = htons(DEFAULT_KPASSWD_PORT); if (socktype != SOCK_STREAM) s->socktype = socktype; if (s->hostname != NULL) s->port = kpasswd_port; else if (s->family == AF_INET) ss2sin(&s->addr)->sin_port = kpasswd_port; else if (s->family == AF_INET6) ss2sin6(&s->addr)->sin6_port = kpasswd_port; } } } return (code); }
static void *cvtaddr (struct sockaddr_storage *a, struct addrpair *ap) { switch (ss2sa(a)->sa_family) { case AF_INET: SET (ap->port, ss2sin(a)->sin_port, ADDRTYPE_IPPORT); SET (ap->addr, ss2sin(a)->sin_addr, ADDRTYPE_INET); return a; case AF_INET6: SET (ap->port, ss2sin6(a)->sin6_port, ADDRTYPE_IPPORT); if (IN6_IS_ADDR_V4MAPPED (&ss2sin6(a)->sin6_addr)) { ap->addr.addrtype = ADDRTYPE_INET; ap->addr.contents = 12 + (krb5_octet *) &ss2sin6(a)->sin6_addr; ap->addr.length = 4; } else SET (ap->addr, ss2sin6(a)->sin6_addr, ADDRTYPE_INET6); return a; default: return 0; } }
static void one_addr(krb5_address *a) { struct sockaddr_storage ss; struct sockaddr_in *sinp; struct sockaddr_in6 *sin6p; int err; char namebuf[NI_MAXHOST]; memset(&ss, 0, sizeof(ss)); switch (a->addrtype) { case ADDRTYPE_INET: if (a->length != 4) { printf(_("broken address (type %d length %d)"), a->addrtype, a->length); return; } sinp = ss2sin(&ss); sinp->sin_family = AF_INET; memcpy(&sinp->sin_addr, a->contents, 4); break; case ADDRTYPE_INET6: if (a->length != 16) { printf(_("broken address (type %d length %d)"), a->addrtype, a->length); return; } sin6p = ss2sin6(&ss); sin6p->sin6_family = AF_INET6; memcpy(&sin6p->sin6_addr, a->contents, 16); break; default: printf(_("unknown addrtype %d"), a->addrtype); return; } namebuf[0] = 0; err = getnameinfo(ss2sa(&ss), sa_socklen(ss2sa(&ss)), namebuf, sizeof(namebuf), 0, 0, no_resolve ? NI_NUMERICHOST : 0U); if (err) { printf(_("unprintable address (type %d, error %d %s)"), a->addrtype, err, gai_strerror(err)); return; } printf("%s", namebuf); }
/* ** The logic for setting and changing a password is mostly the same ** change_set_password handles both cases ** if set_password_for is NULL, then a password change is performed, ** otherwise, the password is set for the principal indicated in set_password_for */ static krb5_error_code change_set_password(krb5_context context, krb5_creds *creds, char *newpw, krb5_principal set_password_for, int *result_code, krb5_data *result_code_string, krb5_data *result_string) { krb5_data chpw_rep; krb5_address remote_kaddr; krb5_boolean use_tcp = 0; GETSOCKNAME_ARG3_TYPE addrlen; krb5_error_code code = 0; char *code_string; int local_result_code; struct sendto_callback_context callback_ctx; struct sendto_callback_info callback_info; struct sockaddr_storage remote_addr; struct serverlist sl = SERVERLIST_INIT; memset(&chpw_rep, 0, sizeof(krb5_data)); memset( &callback_ctx, 0, sizeof(struct sendto_callback_context)); callback_ctx.context = context; callback_ctx.newpw = newpw; callback_ctx.set_password_for = set_password_for; if ((code = krb5_auth_con_init(callback_ctx.context, &callback_ctx.auth_context))) goto cleanup; if ((code = krb5_mk_req_extended(callback_ctx.context, &callback_ctx.auth_context, AP_OPTS_USE_SUBKEY, NULL, creds, &callback_ctx.ap_req))) goto cleanup; callback_ctx.remote_seq_num = callback_ctx.auth_context->remote_seq_number; callback_ctx.local_seq_num = callback_ctx.auth_context->local_seq_number; do { int socktype = (use_tcp ? SOCK_STREAM : SOCK_DGRAM); code = locate_kpasswd(callback_ctx.context, &creds->server->realm, &sl, socktype); if (code) break; addrlen = sizeof(remote_addr); callback_info.data = &callback_ctx; callback_info.pfn_callback = kpasswd_sendto_msg_callback; callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup; krb5_free_data_contents(callback_ctx.context, &chpw_rep); code = k5_sendto(callback_ctx.context, NULL, &sl, socktype, 0, &callback_info, &chpw_rep, ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL); if (code) { /* * Here we may want to switch to TCP on some errors. * right? */ break; } if (remote_addr.ss_family == AF_INET) { remote_kaddr.addrtype = ADDRTYPE_INET; remote_kaddr.length = sizeof(ss2sin(&remote_addr)->sin_addr); remote_kaddr.contents = (krb5_octet *) &ss2sin(&remote_addr)->sin_addr; } else if (remote_addr.ss_family == AF_INET6) { remote_kaddr.addrtype = ADDRTYPE_INET6; remote_kaddr.length = sizeof(ss2sin6(&remote_addr)->sin6_addr); remote_kaddr.contents = (krb5_octet *) &ss2sin6(&remote_addr)->sin6_addr; } else { break; } if ((code = krb5_auth_con_setaddrs(callback_ctx.context, callback_ctx.auth_context, NULL, &remote_kaddr))) break; code = krb5int_rd_chpw_rep(callback_ctx.context, callback_ctx.auth_context, &chpw_rep, &local_result_code, result_string); if (code) { if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) { k5_free_serverlist(&sl); use_tcp = 1; continue; } break; } if (result_code) *result_code = local_result_code; if (result_code_string) { code = krb5_chpw_result_code_string(callback_ctx.context, local_result_code, &code_string); if (code) goto cleanup; result_code_string->length = strlen(code_string); result_code_string->data = malloc(result_code_string->length); if (result_code_string->data == NULL) { code = ENOMEM; goto cleanup; } strncpy(result_code_string->data, code_string, result_code_string->length); } if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) { k5_free_serverlist(&sl); use_tcp = 1; } else { break; } } while (TRUE); cleanup: if (callback_ctx.auth_context != NULL) krb5_auth_con_free(callback_ctx.context, callback_ctx.auth_context); k5_free_serverlist(&sl); krb5_free_data_contents(callback_ctx.context, &callback_ctx.ap_req); krb5_free_data_contents(callback_ctx.context, &chpw_rep); return(code); }
static int kpasswd_sendto_msg_callback(SOCKET fd, void *data, krb5_data *message) { krb5_error_code code = 0; struct sockaddr_storage local_addr; krb5_address local_kaddr; struct sendto_callback_context *ctx = data; GETSOCKNAME_ARG3_TYPE addrlen; krb5_data output; memset (message, 0, sizeof(krb5_data)); /* * We need the local addr from the connection socket */ addrlen = sizeof(local_addr); if (getsockname(fd, ss2sa(&local_addr), &addrlen) < 0) { code = SOCKET_ERRNO; goto cleanup; } /* some brain-dead OS's don't return useful information from * the getsockname call. Namely, windows and solaris. */ if (local_addr.ss_family == AF_INET && ss2sin(&local_addr)->sin_addr.s_addr != 0) { local_kaddr.addrtype = ADDRTYPE_INET; local_kaddr.length = sizeof(ss2sin(&local_addr)->sin_addr); local_kaddr.contents = (krb5_octet *) &ss2sin(&local_addr)->sin_addr; } else if (local_addr.ss_family == AF_INET6 && memcmp(ss2sin6(&local_addr)->sin6_addr.s6_addr, in6addr_any.s6_addr, sizeof(in6addr_any.s6_addr)) != 0) { local_kaddr.addrtype = ADDRTYPE_INET6; local_kaddr.length = sizeof(ss2sin6(&local_addr)->sin6_addr); local_kaddr.contents = (krb5_octet *) &ss2sin6(&local_addr)->sin6_addr; } else { krb5_address **addrs; code = krb5_os_localaddr(ctx->context, &addrs); if (code) goto cleanup; local_kaddr.magic = addrs[0]->magic; local_kaddr.addrtype = addrs[0]->addrtype; local_kaddr.length = addrs[0]->length; local_kaddr.contents = k5memdup(addrs[0]->contents, addrs[0]->length, &code); krb5_free_addresses(ctx->context, addrs); if (local_kaddr.contents == NULL) goto cleanup; } /* * TBD: Does this tamper w/ the auth context in such a way * to break us? Yes - provide 1 per conn-state / host... */ if ((code = krb5_auth_con_setaddrs(ctx->context, ctx->auth_context, &local_kaddr, NULL))) goto cleanup; ctx->auth_context->remote_seq_number = ctx->remote_seq_num; ctx->auth_context->local_seq_number = ctx->local_seq_num; if (ctx->set_password_for) code = krb5int_mk_setpw_req(ctx->context, ctx->auth_context, &ctx->ap_req, ctx->set_password_for, ctx->newpw, &output); else code = krb5int_mk_chpw_req(ctx->context, ctx->auth_context, &ctx->ap_req, ctx->newpw, &output); if (code) goto cleanup; message->length = output.length; message->data = output.data; cleanup: return code; }
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; }