KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host, struct addrinfo **ai) { int ret = 0; if (host->ai == NULL) { struct addrinfo hints; char portstr[NI_MAXSERV]; char *hostname = host->hostname; snprintf (portstr, sizeof(portstr), "%d", host->port); make_hints(&hints, host->proto); /** * First try this as an IP address, this allows us to add a * dot at the end to stop using the search domains. */ hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV; ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai); if (ret == 0) goto out; /** * If the hostname contains a dot, assumes it's a FQDN and * don't use search domains since that might be painfully slow * when machine is disconnected from that network. */ hints.ai_flags &= ~(AI_NUMERICHOST); if (strchr(hostname, '.') && hostname[strlen(hostname) - 1] != '.') { ret = asprintf(&hostname, "%s.", host->hostname); if (ret < 0 || hostname == NULL) return ENOMEM; } ret = getaddrinfo(hostname, portstr, &hints, &host->ai); if (hostname != host->hostname) free(hostname); if (ret) { ret = krb5_eai_to_heim_errno(ret, errno); goto out; } } out: *ai = host->ai; return ret; }
krb5_error_code KRB5_LIB_FUNCTION krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host, struct addrinfo **ai) { struct addrinfo hints; char portstr[NI_MAXSERV]; int ret; if (host->ai == NULL) { make_hints(&hints, host->proto); snprintf (portstr, sizeof(portstr), "%d", host->port); ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai); if (ret) return krb5_eai_to_heim_errno(ret, errno); } *ai = host->ai; return 0; }
void start_server(krb5_context context, const char *port_str) { int e; struct kadm_port *p; krb5_socket_t *socks = NULL, *tmp; unsigned int num_socks = 0; int i; if (port_str == NULL) port_str = "+"; parse_ports(context, port_str); for(p = kadm_ports; p; p = p->next) { struct addrinfo hints, *ai, *ap; char portstr[32]; memset (&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; e = getaddrinfo(NULL, p->port, &hints, &ai); if(e) { snprintf(portstr, sizeof(portstr), "%u", p->def_port); e = getaddrinfo(NULL, portstr, &hints, &ai); } if(e) { krb5_warn(context, krb5_eai_to_heim_errno(e, errno), "%s", portstr); continue; } i = 0; for(ap = ai; ap; ap = ap->ai_next) i++; tmp = realloc(socks, (num_socks + i) * sizeof(*socks)); if(tmp == NULL) { krb5_warnx(context, "failed to reallocate %lu bytes", (unsigned long)(num_socks + i) * sizeof(*socks)); continue; } socks = tmp; for(ap = ai; ap; ap = ap->ai_next) { krb5_socket_t s = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol); if(rk_IS_BAD_SOCKET(s)) { krb5_warn(context, rk_SOCK_ERRNO, "socket"); continue; } socket_set_reuseaddr(s, 1); socket_set_ipv6only(s, 1); if (rk_IS_SOCKET_ERROR(bind (s, ap->ai_addr, ap->ai_addrlen))) { krb5_warn(context, rk_SOCK_ERRNO, "bind"); rk_closesocket(s); continue; } if (rk_IS_SOCKET_ERROR(listen (s, SOMAXCONN))) { krb5_warn(context, rk_SOCK_ERRNO, "listen"); rk_closesocket(s); continue; } socks[num_socks++] = s; } freeaddrinfo (ai); } if(num_socks == 0) krb5_errx(context, 1, "no sockets to listen to - exiting"); wait_for_connection(context, socks, num_socks); }
krb5_error_code KRB5_LIB_FUNCTION krb5_get_forwarded_creds (krb5_context context, krb5_auth_context auth_context, krb5_ccache ccache, krb5_flags flags, const char *hostname, krb5_creds *in_creds, krb5_data *out_data) { krb5_error_code ret; krb5_creds *out_creds; krb5_addresses addrs, *paddrs; KRB_CRED cred; KrbCredInfo *krb_cred_info; EncKrbCredPart enc_krb_cred_part; size_t len; unsigned char *buf; size_t buf_size; krb5_kdc_flags kdc_flags; krb5_crypto crypto; struct addrinfo *ai; int save_errno; krb5_creds *ticket; char *realm; if (in_creds->client && in_creds->client->realm) realm = in_creds->client->realm; else realm = in_creds->server->realm; addrs.len = 0; addrs.val = NULL; paddrs = &addrs; /* * If tickets are address-less, forward address-less tickets. */ ret = _krb5_get_krbtgt (context, ccache, realm, &ticket); if(ret == 0) { if (ticket->addresses.len == 0) paddrs = NULL; krb5_free_creds (context, ticket); } if (paddrs != NULL) { ret = getaddrinfo (hostname, NULL, NULL, &ai); if (ret) { save_errno = errno; krb5_set_error_string(context, "resolving %s: %s", hostname, gai_strerror(ret)); return krb5_eai_to_heim_errno(ret, save_errno); } ret = add_addrs (context, &addrs, ai); freeaddrinfo (ai); if (ret) return ret; } kdc_flags.b = int2KDCOptions(flags); ret = krb5_get_kdc_cred (context, ccache, kdc_flags, paddrs, NULL, in_creds, &out_creds); krb5_free_addresses (context, &addrs); if (ret) { return ret; } memset (&cred, 0, sizeof(cred)); cred.pvno = 5; cred.msg_type = krb_cred; ALLOC_SEQ(&cred.tickets, 1); if (cred.tickets.val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out2; } ret = decode_Ticket(out_creds->ticket.data, out_creds->ticket.length, cred.tickets.val, &len); if (ret) goto out3; memset (&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part)); ALLOC_SEQ(&enc_krb_cred_part.ticket_info, 1); if (enc_krb_cred_part.ticket_info.val == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out4; } if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_timestamp sec; int32_t usec; krb5_us_timeofday (context, &sec, &usec); ALLOC(enc_krb_cred_part.timestamp, 1); if (enc_krb_cred_part.timestamp == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out4; } *enc_krb_cred_part.timestamp = sec; ALLOC(enc_krb_cred_part.usec, 1); if (enc_krb_cred_part.usec == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out4; } *enc_krb_cred_part.usec = usec; } else { enc_krb_cred_part.timestamp = NULL; enc_krb_cred_part.usec = NULL; } if (auth_context->local_address && auth_context->local_port) { krb5_boolean noaddr; krb5_const_realm realm; realm = krb5_principal_get_realm(context, out_creds->server); krb5_appdefault_boolean(context, NULL, realm, "no-addresses", paddrs == NULL, &noaddr); if (!noaddr) { ret = krb5_make_addrport (context, &enc_krb_cred_part.s_address, auth_context->local_address, auth_context->local_port); if (ret) goto out4; } } if (auth_context->remote_address) { if (auth_context->remote_port) { krb5_boolean noaddr; krb5_const_realm realm; realm = krb5_principal_get_realm(context, out_creds->server); /* Is this correct, and should we use the paddrs == NULL trick here as well? Having an address-less ticket may indicate that we don't know our own global address, but it does not necessary mean that we don't know the server's. */ krb5_appdefault_boolean(context, NULL, realm, "no-addresses", FALSE, &noaddr); if (!noaddr) { ret = krb5_make_addrport (context, &enc_krb_cred_part.r_address, auth_context->remote_address, auth_context->remote_port); if (ret) goto out4; } } else { ALLOC(enc_krb_cred_part.r_address, 1); if (enc_krb_cred_part.r_address == NULL) { ret = ENOMEM; krb5_set_error_string(context, "malloc: out of memory"); goto out4; } ret = krb5_copy_address (context, auth_context->remote_address, enc_krb_cred_part.r_address); if (ret) goto out4; } } /* fill ticket_info.val[0] */ enc_krb_cred_part.ticket_info.len = 1; krb_cred_info = enc_krb_cred_part.ticket_info.val; copy_EncryptionKey (&out_creds->session, &krb_cred_info->key); ALLOC(krb_cred_info->prealm, 1); copy_Realm (&out_creds->client->realm, krb_cred_info->prealm); ALLOC(krb_cred_info->pname, 1); copy_PrincipalName(&out_creds->client->name, krb_cred_info->pname); ALLOC(krb_cred_info->flags, 1); *krb_cred_info->flags = out_creds->flags.b; ALLOC(krb_cred_info->authtime, 1); *krb_cred_info->authtime = out_creds->times.authtime; ALLOC(krb_cred_info->starttime, 1); *krb_cred_info->starttime = out_creds->times.starttime; ALLOC(krb_cred_info->endtime, 1); *krb_cred_info->endtime = out_creds->times.endtime; ALLOC(krb_cred_info->renew_till, 1); *krb_cred_info->renew_till = out_creds->times.renew_till; ALLOC(krb_cred_info->srealm, 1); copy_Realm (&out_creds->server->realm, krb_cred_info->srealm); ALLOC(krb_cred_info->sname, 1); copy_PrincipalName (&out_creds->server->name, krb_cred_info->sname); ALLOC(krb_cred_info->caddr, 1); copy_HostAddresses (&out_creds->addresses, krb_cred_info->caddr); krb5_free_creds (context, out_creds); /* encode EncKrbCredPart */ ASN1_MALLOC_ENCODE(EncKrbCredPart, buf, buf_size, &enc_krb_cred_part, &len, ret); free_EncKrbCredPart (&enc_krb_cred_part); if (ret) { free_KRB_CRED(&cred); return ret; } if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); if (auth_context->flags & KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED) { cred.enc_part.etype = ENCTYPE_NULL; cred.enc_part.kvno = NULL; cred.enc_part.cipher.data = buf; cred.enc_part.cipher.length = buf_size; } else { krb5_keyblock *key; if (auth_context->local_subkey) key = auth_context->local_subkey; else if (auth_context->remote_subkey) key = auth_context->remote_subkey; else key = auth_context->keyblock; ret = krb5_crypto_init(context, key, 0, &crypto); if (ret) { free(buf); free_KRB_CRED(&cred); return ret; } ret = krb5_encrypt_EncryptedData (context, crypto, KRB5_KU_KRB_CRED, buf, len, 0, &cred.enc_part); free(buf); krb5_crypto_destroy(context, crypto); if (ret) { free_KRB_CRED(&cred); return ret; } } ASN1_MALLOC_ENCODE(KRB_CRED, buf, buf_size, &cred, &len, ret); free_KRB_CRED (&cred); if (ret) return ret; if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); out_data->length = len; out_data->data = buf; return 0; out4: free_EncKrbCredPart(&enc_krb_cred_part); out3: free_KRB_CRED(&cred); out2: krb5_free_creds (context, out_creds); return ret; }
static int send_via_proxy (krb5_context context, const krb5_krbhst_info *hi, const krb5_data *send_data, krb5_data *receive) { char *proxy2 = strdup(context->http_proxy); char *proxy = proxy2; char *prefix = NULL; char *colon; struct addrinfo hints; struct addrinfo *ai, *a; int ret; krb5_socket_t s = rk_INVALID_SOCKET; char portstr[NI_MAXSERV]; if (proxy == NULL) return ENOMEM; if (strncmp (proxy, "http://", 7) == 0) proxy += 7; colon = strchr(proxy, ':'); if(colon != NULL) *colon++ = '\0'; memset (&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; snprintf (portstr, sizeof(portstr), "%d", ntohs(init_port (colon, htons(80)))); ret = getaddrinfo (proxy, portstr, &hints, &ai); free (proxy2); if (ret) return krb5_eai_to_heim_errno(ret, errno); for (a = ai; a != NULL; a = a->ai_next) { s = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol); if (s < 0) continue; rk_cloexec(s); if (timed_connect (s, a, context->kdc_timeout) < 0) { rk_closesocket (s); continue; } break; } if (a == NULL) { freeaddrinfo (ai); return 1; } freeaddrinfo (ai); ret = asprintf(&prefix, "http://%s/", hi->hostname); if(ret < 0 || prefix == NULL) { close(s); return 1; } ret = send_and_recv_http(s, context->kdc_timeout, prefix, send_data, receive); rk_closesocket (s); free(prefix); if(ret == 0 && receive->length != 0) return 0; return 1; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_get_forwarded_creds (krb5_context context, krb5_auth_context auth_context, krb5_ccache ccache, krb5_flags flags, const char *hostname, krb5_creds *in_creds, krb5_data *out_data) { krb5_error_code ret; krb5_creds *out_creds; krb5_addresses addrs, *paddrs; KRB_CRED cred; KrbCredInfo *krb_cred_info; EncKrbCredPart enc_krb_cred_part; size_t len; unsigned char *buf; size_t buf_size; krb5_kdc_flags kdc_flags; krb5_crypto crypto; struct addrinfo *ai; krb5_creds *ticket; paddrs = NULL; addrs.len = 0; addrs.val = NULL; ret = krb5_get_credentials(context, 0, ccache, in_creds, &ticket); if(ret == 0) { if (ticket->addresses.len) paddrs = &addrs; krb5_free_creds (context, ticket); } else { krb5_boolean noaddr; krb5_appdefault_boolean(context, NULL, krb5_principal_get_realm(context, in_creds->client), "no-addresses", KRB5_ADDRESSLESS_DEFAULT, &noaddr); if (!noaddr) paddrs = &addrs; } /* * If tickets have addresses, get the address of the remote host. */ if (paddrs != NULL) { ret = getaddrinfo (hostname, NULL, NULL, &ai); if (ret) { krb5_error_code ret2 = krb5_eai_to_heim_errno(ret, errno); krb5_set_error_message(context, ret2, N_("resolving host %s failed: %s", "hostname, error"), hostname, gai_strerror(ret)); return ret2; } ret = add_addrs (context, &addrs, ai); freeaddrinfo (ai); if (ret) return ret; } kdc_flags.b = int2KDCOptions(flags); ret = krb5_get_kdc_cred (context, ccache, kdc_flags, paddrs, NULL, in_creds, &out_creds); krb5_free_addresses (context, &addrs); if (ret) return ret; memset (&cred, 0, sizeof(cred)); cred.pvno = 5; cred.msg_type = krb_cred; ALLOC_SEQ(&cred.tickets, 1); if (cred.tickets.val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out2; } ret = decode_Ticket(out_creds->ticket.data, out_creds->ticket.length, cred.tickets.val, &len); if (ret) goto out3; memset (&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part)); ALLOC_SEQ(&enc_krb_cred_part.ticket_info, 1); if (enc_krb_cred_part.ticket_info.val == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out4; } if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) { krb5_timestamp sec; int32_t usec; krb5_us_timeofday (context, &sec, &usec); ALLOC(enc_krb_cred_part.timestamp, 1); if (enc_krb_cred_part.timestamp == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out4; } *enc_krb_cred_part.timestamp = sec; ALLOC(enc_krb_cred_part.usec, 1); if (enc_krb_cred_part.usec == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out4; } *enc_krb_cred_part.usec = usec; } else { enc_krb_cred_part.timestamp = NULL; enc_krb_cred_part.usec = NULL; } if (auth_context->local_address && auth_context->local_port && paddrs) { ret = krb5_make_addrport (context, &enc_krb_cred_part.s_address, auth_context->local_address, auth_context->local_port); if (ret) goto out4; } if (auth_context->remote_address) { if (auth_context->remote_port) { krb5_boolean noaddr; krb5_const_realm srealm; srealm = krb5_principal_get_realm(context, out_creds->server); /* Is this correct, and should we use the paddrs == NULL trick here as well? Having an address-less ticket may indicate that we don't know our own global address, but it does not necessary mean that we don't know the server's. */ krb5_appdefault_boolean(context, NULL, srealm, "no-addresses", FALSE, &noaddr); if (!noaddr) { ret = krb5_make_addrport (context, &enc_krb_cred_part.r_address, auth_context->remote_address, auth_context->remote_port); if (ret) goto out4; } } else { ALLOC(enc_krb_cred_part.r_address, 1); if (enc_krb_cred_part.r_address == NULL) { ret = ENOMEM; krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); goto out4; } ret = krb5_copy_address (context, auth_context->remote_address, enc_krb_cred_part.r_address); if (ret) goto out4; } } /* fill ticket_info.val[0] */ enc_krb_cred_part.ticket_info.len = 1; krb_cred_info = enc_krb_cred_part.ticket_info.val; copy_EncryptionKey (&out_creds->session, &krb_cred_info->key); ALLOC(krb_cred_info->prealm, 1); copy_Realm (&out_creds->client->realm, krb_cred_info->prealm); ALLOC(krb_cred_info->pname, 1); copy_PrincipalName(&out_creds->client->name, krb_cred_info->pname); ALLOC(krb_cred_info->flags, 1); *krb_cred_info->flags = out_creds->flags.b; ALLOC(krb_cred_info->authtime, 1); *krb_cred_info->authtime = out_creds->times.authtime; ALLOC(krb_cred_info->starttime, 1); *krb_cred_info->starttime = out_creds->times.starttime; ALLOC(krb_cred_info->endtime, 1); *krb_cred_info->endtime = out_creds->times.endtime; ALLOC(krb_cred_info->renew_till, 1); *krb_cred_info->renew_till = out_creds->times.renew_till; ALLOC(krb_cred_info->srealm, 1); copy_Realm (&out_creds->server->realm, krb_cred_info->srealm); ALLOC(krb_cred_info->sname, 1); copy_PrincipalName (&out_creds->server->name, krb_cred_info->sname); ALLOC(krb_cred_info->caddr, 1); copy_HostAddresses (&out_creds->addresses, krb_cred_info->caddr); krb5_free_creds (context, out_creds); /* encode EncKrbCredPart */ ASN1_MALLOC_ENCODE(EncKrbCredPart, buf, buf_size, &enc_krb_cred_part, &len, ret); free_EncKrbCredPart (&enc_krb_cred_part); if (ret) { free_KRB_CRED(&cred); return ret; } if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); /** * Some older of the MIT gssapi library used clear-text tickets * (warped inside AP-REQ encryption), use the krb5_auth_context * flag KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED to support those * tickets. The session key is used otherwise to encrypt the * forwarded ticket. */ if (auth_context->flags & KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED) { cred.enc_part.etype = KRB5_ENCTYPE_NULL; cred.enc_part.kvno = NULL; cred.enc_part.cipher.data = buf; cred.enc_part.cipher.length = buf_size; } else { /* * Here older versions then 0.7.2 of Heimdal used the local or * remote subkey. That is wrong, the session key should be * used. Heimdal 0.7.2 and newer have code to try both in the * receiving end. */ ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto); if (ret) { free(buf); free_KRB_CRED(&cred); return ret; } ret = krb5_encrypt_EncryptedData (context, crypto, KRB5_KU_KRB_CRED, buf, len, 0, &cred.enc_part); free(buf); krb5_crypto_destroy(context, crypto); if (ret) { free_KRB_CRED(&cred); return ret; } } ASN1_MALLOC_ENCODE(KRB_CRED, buf, buf_size, &cred, &len, ret); free_KRB_CRED (&cred); if (ret) return ret; if(buf_size != len) krb5_abortx(context, "internal error in ASN.1 encoder"); out_data->length = len; out_data->data = buf; return 0; out4: free_EncKrbCredPart(&enc_krb_cred_part); out3: free_KRB_CRED(&cred); out2: krb5_free_creds (context, out_creds); return ret; }
krb5_error_code KRB5_LIB_FUNCTION krb5_parse_address(krb5_context context, const char *string, krb5_addresses *addresses) { int i, n; struct addrinfo *ai, *a; int error; int save_errno; addresses->len = 0; addresses->val = NULL; for(i = 0; i < num_addrs; i++) { if(at[i].parse_addr) { krb5_address addr; if((*at[i].parse_addr)(context, string, &addr) == 0) { ALLOC_SEQ(addresses, 1); if (addresses->val == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } addresses->val[0] = addr; return 0; } } } error = getaddrinfo (string, NULL, NULL, &ai); if (error) { krb5_error_code ret2; save_errno = errno; ret2 = krb5_eai_to_heim_errno(error, save_errno); krb5_set_error_message (context, ret2, "%s: %s", string, gai_strerror(error)); return ret2; } n = 0; for (a = ai; a != NULL; a = a->ai_next) ++n; ALLOC_SEQ(addresses, n); if (addresses->val == NULL) { krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); freeaddrinfo(ai); return ENOMEM; } addresses->len = 0; for (a = ai, i = 0; a != NULL; a = a->ai_next) { if (krb5_sockaddr2address (context, ai->ai_addr, &addresses->val[i])) continue; if(krb5_address_search(context, &addresses->val[i], addresses)) { krb5_free_address(context, &addresses->val[i]); continue; } i++; addresses->len = i; } freeaddrinfo (ai); return 0; }