static int do_v5 (const char *host, int port, const char *user, const char *filename, const char *header_str, int leavep, int verbose, int forkp) { krb5_error_code ret; krb5_auth_context auth_context = NULL; krb5_principal server; const char *estr; int s; s = do_connect (host, port, 1); if (s < 0) return 1; ret = krb5_sname_to_principal (context, host, "pop", KRB5_NT_SRV_HST, &server); if (ret) { estr = krb5_get_error_message(context, ret); warnx ("krb5_sname_to_principal: %s", estr); krb5_free_error_message(context, estr); return 1; } ret = krb5_sendauth (context, &auth_context, &s, "KPOPV1.0", NULL, server, 0, NULL, NULL, NULL, NULL, NULL, NULL); krb5_free_principal (context, server); if (ret) { estr = krb5_get_error_message(context, ret); warnx ("krb5_sendauth: %s", estr); krb5_free_error_message(context, estr); return 1; } return doit (s, host, user, filename, header_str, leavep, verbose, forkp); }
static void kerberos_authenticate(krb5_context context, krb5_auth_context *auth_context, int fd, krb5_principal me, krb5_creds **new_creds) { krb5_error_code retval; krb5_error *error = NULL; krb5_ap_rep_enc_part *rep_result; retval = krb5_auth_con_init(context, auth_context); if (retval) exit(1); krb5_auth_con_setflags(context, *auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); retval = krb5_auth_con_setaddrs(context, *auth_context, sender_addr, receiver_addr); if (retval) { com_err(progname, retval, _("in krb5_auth_con_setaddrs")); exit(1); } retval = krb5_sendauth(context, auth_context, &fd, kprop_version, me, creds.server, AP_OPTS_MUTUAL_REQUIRED, NULL, &creds, NULL, &error, &rep_result, new_creds); if (retval) { com_err(progname, retval, _("while authenticating to server")); if (error != NULL) { if (error->error == KRB_ERR_GENERIC) { if (error->text.data) { fprintf(stderr, _("Generic remote error: %s\n"), error->text.data); } } else if (error->error) { com_err(progname, (krb5_error_code)error->error + ERROR_TABLE_BASE_krb5, _("signalled from server")); if (error->text.data) { fprintf(stderr, _("Error text from server: %s\n"), error->text.data); } } krb5_free_error(context, error); } exit(1); } krb5_free_ap_rep_enc_part(context, rep_result); }
static int doit_v5 (char *host, int port) { krb5_error_code ret; krb5_context context; krb5_auth_context auth_context = NULL; krb5_principal server; int s = get_socket (host, port); ret = krb5_init_context (&context); if (ret) errx (1, "krb5_init_context failed: %d", ret); ret = krb5_sname_to_principal (context, host, "pop", KRB5_NT_SRV_HST, &server); if (ret) { warnx ("krb5_sname_to_principal: %s", krb5_get_err_text (context, ret)); return 1; } ret = krb5_sendauth (context, &auth_context, &s, "KPOPV1.0", NULL, server, 0, NULL, NULL, NULL, NULL, NULL, NULL); if (ret) { warnx ("krb5_sendauth: %s", krb5_get_err_text (context, ret)); return 1; } loop (s); return 0; }
/* * pg_krb5_sendauth -- client routine to send authentication information to * the server */ static int pg_krb5_sendauth(PGconn *conn) { krb5_error_code retval; int ret; krb5_principal server; krb5_auth_context auth_context = NULL; krb5_error *err_ret = NULL; struct krb5_info info; info.pg_krb5_initialised = 0; if (!(conn->pghost && conn->pghost[0] != '\0')) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("host name must be specified\n")); return STATUS_ERROR; } ret = pg_krb5_init(&conn->errorMessage, &info); if (ret != STATUS_OK) return ret; retval = krb5_sname_to_principal(info.pg_krb5_context, conn->pghost, conn->krbsrvname, KRB5_NT_SRV_HST, &server); if (retval) { printfPQExpBuffer(&conn->errorMessage, "pg_krb5_sendauth: krb5_sname_to_principal: %s\n", error_message(retval)); pg_krb5_destroy(&info); return STATUS_ERROR; } /* * libpq uses a non-blocking socket. But kerberos needs a blocking socket, * and we have to block somehow to do mutual authentication anyway. So we * temporarily make it blocking. */ if (!pg_set_block(conn->sock)) { char sebuf[256]; printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not set socket to blocking mode: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf))); krb5_free_principal(info.pg_krb5_context, server); pg_krb5_destroy(&info); return STATUS_ERROR; } retval = krb5_sendauth(info.pg_krb5_context, &auth_context, (krb5_pointer) & conn->sock, (char *) conn->krbsrvname, info.pg_krb5_client, server, AP_OPTS_MUTUAL_REQUIRED, NULL, 0, /* no creds, use ccache instead */ info.pg_krb5_ccache, &err_ret, NULL, NULL); if (retval) { if (retval == KRB5_SENDAUTH_REJECTED && err_ret) { #if defined(HAVE_KRB5_ERROR_TEXT_DATA) printfPQExpBuffer(&conn->errorMessage, libpq_gettext("Kerberos 5 authentication rejected: %*s\n"), (int) err_ret->text.length, err_ret->text.data); #elif defined(HAVE_KRB5_ERROR_E_DATA) printfPQExpBuffer(&conn->errorMessage, libpq_gettext("Kerberos 5 authentication rejected: %*s\n"), (int) err_ret->e_data->length, (const char *) err_ret->e_data->data); #else #error "bogus configuration" #endif } else { printfPQExpBuffer(&conn->errorMessage, "krb5_sendauth: %s\n", error_message(retval)); } if (err_ret) krb5_free_error(info.pg_krb5_context, err_ret); ret = STATUS_ERROR; } krb5_free_principal(info.pg_krb5_context, server); if (!pg_set_noblock(conn->sock)) { char sebuf[256]; printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not restore non-blocking mode on socket: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf))); ret = STATUS_ERROR; } pg_krb5_destroy(&info); return ret; }
static kadm5_ret_t kadm_connect(kadm5_client_context *ctx) { kadm5_ret_t ret; krb5_principal server; krb5_ccache cc; rk_socket_t s = rk_INVALID_SOCKET; struct addrinfo *ai, *a; struct addrinfo hints; int error; char portstr[NI_MAXSERV]; char *hostname, *slash; char *service_name; krb5_context context = ctx->context; memset (&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; snprintf (portstr, sizeof(portstr), "%u", ntohs(ctx->kadmind_port)); hostname = ctx->admin_server; slash = strchr (hostname, '/'); if (slash != NULL) hostname = slash + 1; error = getaddrinfo (hostname, portstr, &hints, &ai); if (error) { krb5_clear_error_message(context); return KADM5_BAD_SERVER_NAME; } for (a = ai; a != NULL; a = a->ai_next) { s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); if (s < 0) continue; if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { krb5_clear_error_message(context); krb5_warn (context, errno, "connect(%s)", hostname); rk_closesocket (s); continue; } break; } if (a == NULL) { freeaddrinfo (ai); krb5_clear_error_message(context); krb5_warnx (context, "failed to contact %s", hostname); return KADM5_FAILURE; } ret = _kadm5_c_get_cred_cache(context, ctx->client_name, ctx->service_name, NULL, ctx->prompter, ctx->keytab, ctx->ccache, &cc); if(ret) { freeaddrinfo (ai); rk_closesocket(s); return ret; } if (ctx->realm) asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE, ctx->realm); else asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE); if (service_name == NULL) { freeaddrinfo (ai); rk_closesocket(s); krb5_clear_error_message(context); return ENOMEM; } ret = krb5_parse_name(context, service_name, &server); free(service_name); if(ret) { freeaddrinfo (ai); if(ctx->ccache == NULL) krb5_cc_close(context, cc); rk_closesocket(s); return ret; } ctx->ac = NULL; ret = krb5_sendauth(context, &ctx->ac, &s, KADMIN_APPL_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, cc, NULL, NULL, NULL); if(ret == 0) { krb5_data params; kadm5_config_params p; memset(&p, 0, sizeof(p)); if(ctx->realm) { p.mask |= KADM5_CONFIG_REALM; p.realm = ctx->realm; } ret = _kadm5_marshal_params(context, &p, ¶ms); ret = krb5_write_priv_message(context, ctx->ac, &s, ¶ms); krb5_data_free(¶ms); if(ret) { freeaddrinfo (ai); rk_closesocket(s); if(ctx->ccache == NULL) krb5_cc_close(context, cc); return ret; } } else if(ret == KRB5_SENDAUTH_BADAPPLVERS) { rk_closesocket(s); s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); if (s < 0) { freeaddrinfo (ai); krb5_clear_error_message(context); return errno; } if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { rk_closesocket (s); freeaddrinfo (ai); krb5_clear_error_message(context); return errno; } ret = krb5_sendauth(context, &ctx->ac, &s, KADMIN_OLD_APPL_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, cc, NULL, NULL, NULL); } freeaddrinfo (ai); if(ret) { rk_closesocket(s); return ret; } krb5_free_principal(context, server); if(ctx->ccache == NULL) krb5_cc_close(context, cc); ctx->sock = s; return 0; }
static int proto (int sock, const char *hostname, const char *service) { krb5_auth_context auth_context; krb5_error_code status; krb5_principal server; krb5_data data; krb5_data packet; u_int32_t len, net_len; status = krb5_auth_con_init (context, &auth_context); if (status) krb5_err (context, 1, status, "krb5_auth_con_init"); status = krb5_auth_con_setaddrs_from_fd (context, auth_context, &sock); if (status) krb5_err (context, 1, status, "krb5_auth_con_setaddrs_from_fd"); status = krb5_sname_to_principal (context, hostname, service, KRB5_NT_SRV_HST, &server); if (status) krb5_err (context, 1, status, "krb5_sname_to_principal"); status = krb5_sendauth (context, &auth_context, &sock, VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, NULL, NULL, NULL, NULL); if (status) krb5_err (context, 1, status, "krb5_sendauth"); data.data = "hej"; data.length = 3; krb5_data_zero (&packet); status = krb5_mk_safe (context, auth_context, &data, &packet, NULL); if (status) krb5_err (context, 1, status, "krb5_mk_safe"); len = packet.length; net_len = htonl(len); if (krb5_net_write (context, &sock, &net_len, 4) != 4) err (1, "krb5_net_write"); if (krb5_net_write (context, &sock, packet.data, len) != len) err (1, "krb5_net_write"); data.data = "hemligt"; data.length = 7; krb5_data_free (&packet); status = krb5_mk_priv (context, auth_context, &data, &packet, NULL); if (status) krb5_err (context, 1, status, "krb5_mk_priv"); len = packet.length; net_len = htonl(len); if (krb5_net_write (context, &sock, &net_len, 4) != 4) err (1, "krb5_net_write"); if (krb5_net_write (context, &sock, packet.data, len) != len) err (1, "krb5_net_write"); return 0; }
int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_auth_context auth_context; void *kadm_handle; kadm5_server_context *server_context; kadm5_config_params conf; int master_fd; krb5_ccache ccache; krb5_principal server; char **files; int optidx = 0; time_t reconnect_min; time_t backoff; time_t reconnect_max; time_t reconnect; time_t before = 0; const char *master; setprogname(argv[0]); if(getarg(args, num_args, argc, argv, &optidx)) usage(1); if(help_flag) usage(0); if(version_flag) { print_version(NULL); exit(0); } ret = krb5_init_context(&context); if (ret) errx (1, "krb5_init_context failed: %d", ret); setup_signal(); if (config_file == NULL) { asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)); if (config_file == NULL) errx(1, "out of memory"); } ret = krb5_prepend_config_files_default(config_file, &files); if (ret) krb5_err(context, 1, ret, "getting configuration files"); ret = krb5_set_config_files(context, files); krb5_free_config_files(files); if (ret) krb5_err(context, 1, ret, "reading configuration files"); argc -= optidx; argv += optidx; if (argc != 1) usage(1); master = argv[0]; #ifdef SUPPORT_DETACH if (detach_from_console) daemon(0, 0); #endif pidfile (NULL); krb5_openlog (context, "ipropd-slave", &log_facility); krb5_set_warn_dest(context, log_facility); ret = krb5_kt_register(context, &hdb_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); time_before_lost = parse_time (server_time_lost, "s"); if (time_before_lost < 0) krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost); memset(&conf, 0, sizeof(conf)); if(realm) { conf.mask |= KADM5_CONFIG_REALM; conf.realm = realm; } ret = kadm5_init_with_password_ctx (context, KADM5_ADMIN_SERVICE, NULL, KADM5_ADMIN_SERVICE, &conf, 0, 0, &kadm_handle); if (ret) krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); server_context = (kadm5_server_context *)kadm_handle; ret = kadm5_log_init (server_context); if (ret) krb5_err (context, 1, ret, "kadm5_log_init"); get_creds(context, keytab_str, &ccache, master); ret = krb5_sname_to_principal (context, master, IPROP_NAME, KRB5_NT_SRV_HST, &server); if (ret) krb5_err (context, 1, ret, "krb5_sname_to_principal"); auth_context = NULL; master_fd = -1; krb5_appdefault_time(context, config_name, NULL, "reconnect-min", 10, &reconnect_min); krb5_appdefault_time(context, config_name, NULL, "reconnect-max", 300, &reconnect_max); krb5_appdefault_time(context, config_name, NULL, "reconnect-backoff", 10, &backoff); reconnect = reconnect_min; while (!exit_flag) { time_t now, elapsed; int connected = FALSE; now = time(NULL); elapsed = now - before; if (elapsed < reconnect) { time_t left = reconnect - elapsed; krb5_warnx(context, "sleeping %d seconds before " "retrying to connect", (int)left); sleep(left); } before = now; master_fd = connect_to_master (context, master, port_str); if (master_fd < 0) goto retry; reconnect = reconnect_min; if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; krb5_cc_destroy(context, ccache); get_creds(context, keytab_str, &ccache, master); } ret = krb5_sendauth (context, &auth_context, &master_fd, IPROP_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, ccache, NULL, NULL, NULL); if (ret) { krb5_warn (context, ret, "krb5_sendauth"); goto retry; } krb5_warnx(context, "ipropd-slave started at version: %ld", (long)server_context->log_context.version); ret = ihave (context, auth_context, master_fd, server_context->log_context.version); if (ret) goto retry; connected = TRUE; while (connected && !exit_flag) { krb5_data out; krb5_storage *sp; int32_t tmp; fd_set readset; struct timeval to; #ifndef NO_LIMIT_FD_SETSIZE if (master_fd >= FD_SETSIZE) krb5_errx (context, 1, "fd too large"); #endif FD_ZERO(&readset); FD_SET(master_fd, &readset); to.tv_sec = time_before_lost; to.tv_usec = 0; ret = select (master_fd + 1, &readset, NULL, NULL, &to); if (ret < 0) { if (errno == EINTR) continue; else krb5_err (context, 1, errno, "select"); } if (ret == 0) krb5_errx (context, 1, "server didn't send a message " "in %d seconds", time_before_lost); ret = krb5_read_priv_message(context, auth_context, &master_fd, &out); if (ret) { krb5_warn (context, ret, "krb5_read_priv_message"); connected = FALSE; continue; } sp = krb5_storage_from_mem (out.data, out.length); krb5_ret_int32 (sp, &tmp); switch (tmp) { case FOR_YOU : receive (context, sp, server_context); ret = ihave (context, auth_context, master_fd, server_context->log_context.version); if (ret) connected = FALSE; break; case TELL_YOU_EVERYTHING : ret = receive_everything (context, master_fd, server_context, auth_context); if (ret) connected = FALSE; break; case ARE_YOU_THERE : send_im_here (context, master_fd, auth_context); break; case NOW_YOU_HAVE : case I_HAVE : case ONE_PRINC : case I_AM_HERE : default : krb5_warnx (context, "Ignoring command %d", tmp); break; } krb5_storage_free (sp); krb5_data_free (&out); } retry: if (connected == FALSE) krb5_warnx (context, "disconnected for server"); if (exit_flag) krb5_warnx (context, "got an exit signal"); if (master_fd >= 0) close(master_fd); reconnect += backoff; if (reconnect > reconnect_max) reconnect = reconnect_max; } if (0); #ifndef NO_SIGXCPU else if(exit_flag == SIGXCPU) krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); #endif else if(exit_flag == SIGINT || exit_flag == SIGTERM) krb5_warnx(context, "%s terminated", getprogname()); else krb5_warnx(context, "%s unexpected exit reason: %ld", getprogname(), (long)exit_flag); return 0; }
static int proto (int sock, const char *hostname, const char *svc, char *message, size_t len) { krb5_auth_context auth_context; krb5_error_code status; krb5_principal server; krb5_data data; krb5_data data_send; krb5_ccache ccache; krb5_creds creds; krb5_kdc_flags flags; krb5_principal principal; status = krb5_auth_con_init (context, &auth_context); if (status) { krb5_warn (context, status, "krb5_auth_con_init"); return 1; } status = krb5_auth_con_setaddrs_from_fd (context, auth_context, &sock); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_auth_con_setaddr"); return 1; } status = krb5_sname_to_principal (context, hostname, svc, KRB5_NT_SRV_HST, &server); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_sname_to_principal"); return 1; } status = krb5_sendauth (context, &auth_context, &sock, KF_VERSION_1, NULL, server, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, NULL, NULL, NULL, NULL, NULL); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn(context, status, "krb5_sendauth"); return 1; } if (ccache_name == NULL) ccache_name = ""; data_send.data = (void *)remote_name; data_send.length = strlen(remote_name) + 1; status = krb5_write_priv_message(context, auth_context, &sock, &data_send); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_write_message"); return 1; } data_send.data = (void *)ccache_name; data_send.length = strlen(ccache_name)+1; status = krb5_write_priv_message(context, auth_context, &sock, &data_send); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_write_message"); return 1; } memset (&creds, 0, sizeof(creds)); status = krb5_cc_default (context, &ccache); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_cc_default"); return 1; } status = krb5_cc_get_principal (context, ccache, &principal); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_cc_get_principal"); return 1; } creds.client = principal; status = krb5_make_principal (context, &creds.server, principal->realm, KRB5_TGS_NAME, principal->realm, NULL); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_make_principal"); return 1; } creds.times.endtime = 0; flags.i = 0; flags.b.forwarded = 1; flags.b.forwardable = forwardable; status = krb5_get_forwarded_creds (context, auth_context, ccache, flags.i, hostname, &creds, &data); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_get_forwarded_creds"); return 1; } status = krb5_write_priv_message(context, auth_context, &sock, &data); if (status) { krb5_auth_con_free(context, auth_context); krb5_warn (context, status, "krb5_mk_priv"); return 1; } krb5_data_free (&data); status = krb5_read_priv_message(context, auth_context, &sock, &data); krb5_auth_con_free(context, auth_context); if (status) { krb5_warn (context, status, "krb5_mk_priv"); return 1; } if(data.length >= len) { krb5_warnx (context, "returned string is too long, truncating"); memcpy(message, data.data, len); message[len - 1] = '\0'; } else { memcpy(message, data.data, data.length); message[data.length] = '\0'; } krb5_data_free (&data); return(strcmp(message, "ok")); }
/* * Function: socket_connection * * Purpose: Opens the network connection with the mail host, without * doing any sort of I/O with it or anything. * * Arguments: * host The host to which to connect. * flags Option flags. * * Return value: A file descriptor indicating the connection, or -1 * indicating failure, in which case an error has been copied * into pop_error. */ static int socket_connection (char *host, int flags) { struct addrinfo *res, *it; struct addrinfo hints; int ret; struct servent *servent; struct sockaddr_in addr; char found_port = 0; const char *service; int sock; char *realhost; #ifdef KERBEROS #ifdef KERBEROS5 krb5_error_code rem; krb5_context kcontext = 0; krb5_auth_context auth_context = 0; krb5_ccache ccdef; krb5_principal client, server; krb5_error *err_ret; register char *cp; #else KTEXT ticket; MSG_DAT msg_data; CREDENTIALS cred; Key_schedule schedule; int rem; #endif /* KERBEROS5 */ #endif /* KERBEROS */ int try_count = 0; int connect_ok; #ifdef WINDOWSNT { WSADATA winsockData; if (WSAStartup (0x101, &winsockData) == 0) have_winsock = 1; } #endif memset (&addr, 0, sizeof (addr)); addr.sin_family = AF_INET; /** "kpop" service is never used: look for 20060515 to see why **/ #ifdef KERBEROS service = (flags & POP_NO_KERBEROS) ? POP_SERVICE : KPOP_SERVICE; #else service = POP_SERVICE; #endif #ifdef HESIOD if (! (flags & POP_NO_HESIOD)) { servent = hes_getservbyname (service, "tcp"); if (servent) { addr.sin_port = servent->s_port; found_port = 1; } } #endif if (! found_port) { servent = getservbyname (service, "tcp"); if (servent) { addr.sin_port = servent->s_port; } else { /** "kpop" service is never used: look for 20060515 to see why **/ #ifdef KERBEROS addr.sin_port = htons ((flags & POP_NO_KERBEROS) ? POP_PORT : KPOP_PORT); #else addr.sin_port = htons (POP_PORT); #endif } } #define POP_SOCKET_ERROR "Could not create socket for POP connection: " sock = socket (PF_INET, SOCK_STREAM, 0); if (sock < 0) { snprintf (pop_error, ERROR_MAX, "%s%s", POP_SOCKET_ERROR, strerror (errno)); return (-1); } memset (&hints, 0, sizeof (hints)); hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; hints.ai_family = AF_INET; do { ret = getaddrinfo (host, service, &hints, &res); try_count++; if (ret != 0 && (ret != EAI_AGAIN || try_count == 5)) { strcpy (pop_error, "Could not determine POP server's address"); return (-1); } } while (ret != 0); for (it = res; it; it = it->ai_next) if (it->ai_addrlen == sizeof addr) { struct sockaddr_in *in_a = (struct sockaddr_in *) it->ai_addr; addr.sin_addr = in_a->sin_addr; if (! connect (sock, (struct sockaddr *) &addr, sizeof addr)) break; } connect_ok = it != NULL; if (connect_ok) { realhost = alloca (strlen (it->ai_canonname) + 1); strcpy (realhost, it->ai_canonname); } freeaddrinfo (res); #define CONNECT_ERROR "Could not connect to POP server: " if (! connect_ok) { CLOSESOCKET (sock); snprintf (pop_error, ERROR_MAX, "%s%s", CONNECT_ERROR, strerror (errno)); return (-1); } #ifdef KERBEROS #define KRB_ERROR "Kerberos error connecting to POP server: " if (! (flags & POP_NO_KERBEROS)) { #ifdef KERBEROS5 rem = krb5_init_context (&kcontext); if (rem) { krb5error: if (auth_context) krb5_auth_con_free (kcontext, auth_context); if (kcontext) krb5_free_context (kcontext); snprintf (pop_error, ERROR_MAX, "%s%s", KRB_ERROR, error_message (rem)); CLOSESOCKET (sock); return (-1); } rem = krb5_auth_con_init (kcontext, &auth_context); if (rem) goto krb5error; rem = krb5_cc_default (kcontext, &ccdef); if (rem) goto krb5error; rem = krb5_cc_get_principal (kcontext, ccdef, &client); if (rem) goto krb5error; for (cp = realhost; *cp; cp++) *cp = c_tolower (*cp); rem = krb5_sname_to_principal (kcontext, realhost, POP_SERVICE, FALSE, &server); if (rem) goto krb5error; rem = krb5_sendauth (kcontext, &auth_context, (krb5_pointer) &sock, (char *) "KPOPV1.0", client, server, AP_OPTS_MUTUAL_REQUIRED, 0, /* no checksum */ 0, /* no creds, use ccache instead */ ccdef, &err_ret, 0, /* don't need subsession key */ 0); /* don't need reply */ krb5_free_principal (kcontext, server); if (rem) { int pop_error_len = snprintf (pop_error, ERROR_MAX, "%s%s", KRB_ERROR, error_message (rem)); #if defined HAVE_KRB5_ERROR_TEXT if (err_ret && err_ret->text.length) { int errlen = err_ret->text.length; snprintf (pop_error + pop_error_len, ERROR_MAX - pop_error_len, " [server says '%.*s']", errlen, err_ret->text.data); } #elif defined HAVE_KRB5_ERROR_E_TEXT if (err_ret && err_ret->e_text && **err_ret->e_text) snprintf (pop_error + pop_error_len, ERROR_MAX - pop_error_len, " [server says '%s']", *err_ret->e_text); #endif if (err_ret) krb5_free_error (kcontext, err_ret); krb5_auth_con_free (kcontext, auth_context); krb5_free_context (kcontext); CLOSESOCKET (sock); return (-1); } #else /* ! KERBEROS5 */ ticket = (KTEXT) malloc (sizeof (KTEXT_ST)); rem = krb_sendauth (0L, sock, ticket, "pop", realhost, (char *) krb_realmofhost (realhost), (unsigned long) 0, &msg_data, &cred, schedule, (struct sockaddr_in *) 0, (struct sockaddr_in *) 0, "KPOPV0.1"); free ((char *) ticket); if (rem != KSUCCESS) { snprintf (pop_error, ERROR_MAX, "%s%s", KRB_ERROR, krb_err_txt[rem]); CLOSESOCKET (sock); return (-1); } #endif /* KERBEROS5 */ } #endif /* KERBEROS */ return (sock); } /* socket_connection */
static int propagate_database (krb5_context context, int type, const char *database_name, HDB *db, krb5_ccache ccache, int optidx, int argc, char **argv) { krb5_principal server; krb5_error_code ret; int i, failed = 0; for(i = optidx; i < argc; i++){ krb5_auth_context auth_context; int fd; struct prop_data pd; krb5_data data; char *port, portstr[NI_MAXSERV]; char *host = argv[i]; port = strchr(host, ':'); if(port == NULL) { snprintf(portstr, sizeof(portstr), "%u", ntohs(krb5_getportbyname (context, "hprop", "tcp", HPROP_PORT))); port = portstr; } else *port++ = '\0'; fd = open_socket(context, host, port); if(fd < 0) { failed++; krb5_warn (context, errno, "connect %s", host); continue; } ret = krb5_sname_to_principal(context, argv[i], HPROP_NAME, KRB5_NT_SRV_HST, &server); if(ret) { failed++; krb5_warn(context, ret, "krb5_sname_to_principal(%s)", host); close(fd); continue; } if (local_realm) { krb5_realm my_realm; krb5_get_default_realm(context,&my_realm); krb5_principal_set_realm(context,server,my_realm); krb5_xfree(my_realm); } auth_context = NULL; ret = krb5_sendauth(context, &auth_context, &fd, HPROP_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, /* in_data */ NULL, /* in_creds */ ccache, NULL, NULL, NULL); krb5_free_principal(context, server); if(ret) { failed++; krb5_warn(context, ret, "krb5_sendauth (%s)", host); close(fd); goto next_host; } pd.context = context; pd.auth_context = auth_context; pd.sock = fd; ret = iterate (context, database_name, db, type, &pd); if (ret) { krb5_warnx(context, "iterate to host %s failed", host); failed++; goto next_host; } krb5_data_zero (&data); ret = krb5_write_priv_message(context, auth_context, &fd, &data); if(ret) { krb5_warn(context, ret, "krb5_write_priv_message"); failed++; goto next_host; } ret = krb5_read_priv_message(context, auth_context, &fd, &data); if(ret) { krb5_warn(context, ret, "krb5_read_priv_message: %s", host); failed++; goto next_host; } else krb5_data_free (&data); next_host: krb5_auth_con_free(context, auth_context); close(fd); } if (failed) return 1; return 0; }
static int proto (int sock, const char *service) { struct sockaddr_in remote, local; socklen_t addrlen; krb5_address remote_addr, local_addr; krb5_ccache ccache; krb5_auth_context auth_context; krb5_error_code status; krb5_data packet; krb5_data data; krb5_data client_name; krb5_creds in_creds, *out_creds; addrlen = sizeof(local); if (getsockname (sock, (struct sockaddr *)&local, &addrlen) < 0 || addrlen != sizeof(local)) err (1, "getsockname)"); addrlen = sizeof(remote); if (getpeername (sock, (struct sockaddr *)&remote, &addrlen) < 0 || addrlen != sizeof(remote)) err (1, "getpeername"); status = krb5_auth_con_init (context, &auth_context); if (status) errx (1, "krb5_auth_con_init: %s", krb5_get_err_text(context, status)); local_addr.addr_type = AF_INET; local_addr.address.length = sizeof(local.sin_addr); local_addr.address.data = &local.sin_addr; remote_addr.addr_type = AF_INET; remote_addr.address.length = sizeof(remote.sin_addr); remote_addr.address.data = &remote.sin_addr; status = krb5_auth_con_setaddrs (context, auth_context, &local_addr, &remote_addr); if (status) errx (1, "krb5_auth_con_setaddr: %s", krb5_get_err_text(context, status)); status = krb5_read_message(context, &sock, &client_name); if(status) krb5_err(context, 1, status, "krb5_read_message"); memset(&in_creds, 0, sizeof(in_creds)); status = krb5_cc_default(context, &ccache); if(status) krb5_err(context, 1, status, "krb5_cc_default"); status = krb5_cc_get_principal(context, ccache, &in_creds.client); if(status) krb5_err(context, 1, status, "krb5_cc_get_principal"); status = krb5_read_message(context, &sock, &in_creds.second_ticket); if(status) krb5_err(context, 1, status, "krb5_read_message"); status = krb5_parse_name(context, client_name.data, &in_creds.server); if(status) krb5_err(context, 1, status, "krb5_parse_name"); status = krb5_get_credentials(context, KRB5_GC_USER_USER, ccache, &in_creds, &out_creds); if(status) krb5_err(context, 1, status, "krb5_get_credentials"); status = krb5_cc_default(context, &ccache); if(status) krb5_err(context, 1, status, "krb5_cc_default"); status = krb5_sendauth(context, &auth_context, &sock, VERSION, in_creds.client, in_creds.server, AP_OPTS_USE_SESSION_KEY, NULL, out_creds, ccache, NULL, NULL, NULL); if (status) krb5_err(context, 1, status, "krb5_sendauth"); { char *str; krb5_unparse_name(context, in_creds.server, &str); printf ("User is `%s'\n", str); free(str); krb5_unparse_name(context, in_creds.client, &str); printf ("Server is `%s'\n", str); free(str); } krb5_data_zero (&data); krb5_data_zero (&packet); status = krb5_read_message(context, &sock, &packet); if(status) krb5_err(context, 1, status, "krb5_read_message"); status = krb5_rd_safe (context, auth_context, &packet, &data, NULL); if (status) errx (1, "krb5_rd_safe: %s", krb5_get_err_text(context, status)); printf ("safe packet: %.*s\n", (int)data.length, (char *)data.data); status = krb5_read_message(context, &sock, &packet); if(status) krb5_err(context, 1, status, "krb5_read_message"); status = krb5_rd_priv (context, auth_context, &packet, &data, NULL); if (status) errx (1, "krb5_rd_priv: %s", krb5_get_err_text(context, status)); printf ("priv packet: %.*s\n", (int)data.length, (char *)data.data); return 0; }
int main(int argc, char **argv) { krb5_error_code ret, ret2; krb5_context context; krb5_auth_context auth_context; void *kadm_handle; kadm5_server_context *server_context; kadm5_config_params conf; int master_fd; krb5_ccache ccache; krb5_principal server; char **files; int optidx = 0; time_t reconnect_min; time_t backoff; time_t reconnect_max; time_t reconnect; time_t before = 0; int restarter_fd = -1; const char *master; setprogname(argv[0]); if (getarg(args, num_args, argc, argv, &optidx)) usage(1); if (help_flag) usage(0); if (version_flag) { print_version(NULL); exit(0); } if (detach_from_console && daemon_child == -1) roken_detach_prep(argc, argv, "--daemon-child"); rk_pidfile(NULL); ret = krb5_init_context(&context); if (ret) errx (1, "krb5_init_context failed: %d", ret); setup_signal(); if (config_file == NULL) { if (asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)) == -1 || config_file == NULL) errx(1, "out of memory"); } ret = krb5_prepend_config_files_default(config_file, &files); if (ret) krb5_err(context, 1, ret, "getting configuration files"); ret = krb5_set_config_files(context, files); krb5_free_config_files(files); if (ret) krb5_err(context, 1, ret, "reading configuration files"); argc -= optidx; argv += optidx; if (argc != 1) usage(1); master = argv[0]; if (status_file == NULL) { if (asprintf(&status_file, "%s/ipropd-slave-status", hdb_db_dir(context)) < 0 || status_file == NULL) krb5_errx(context, 1, "can't allocate status file buffer"); } krb5_openlog(context, "ipropd-slave", &log_facility); krb5_set_warn_dest(context, log_facility); slave_status(context, status_file, "bootstrapping"); ret = krb5_kt_register(context, &hdb_get_kt_ops); if(ret) krb5_err(context, 1, ret, "krb5_kt_register"); time_before_lost = parse_time (server_time_lost, "s"); if (time_before_lost < 0) krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost); slave_status(context, status_file, "getting credentials from keytab/database"); memset(&conf, 0, sizeof(conf)); if(realm) { conf.mask |= KADM5_CONFIG_REALM; conf.realm = realm; } ret = kadm5_init_with_password_ctx (context, KADM5_ADMIN_SERVICE, NULL, KADM5_ADMIN_SERVICE, &conf, 0, 0, &kadm_handle); if (ret) krb5_err (context, 1, ret, "kadm5_init_with_password_ctx"); server_context = (kadm5_server_context *)kadm_handle; slave_status(context, status_file, "creating log file"); ret = server_context->db->hdb_open(context, server_context->db, O_RDWR | O_CREAT, 0600); if (ret) krb5_err (context, 1, ret, "db->open"); ret = kadm5_log_init (server_context); if (ret) krb5_err (context, 1, ret, "kadm5_log_init"); ret = server_context->db->hdb_close (context, server_context->db); if (ret) krb5_err (context, 1, ret, "db->close"); get_creds(context, keytab_str, &ccache, master); ret = krb5_sname_to_principal (context, master, IPROP_NAME, KRB5_NT_SRV_HST, &server); if (ret) krb5_err (context, 1, ret, "krb5_sname_to_principal"); auth_context = NULL; master_fd = -1; krb5_appdefault_time(context, config_name, NULL, "reconnect-min", 10, &reconnect_min); krb5_appdefault_time(context, config_name, NULL, "reconnect-max", 300, &reconnect_max); krb5_appdefault_time(context, config_name, NULL, "reconnect-backoff", 10, &backoff); reconnect = reconnect_min; slave_status(context, status_file, "ipropd-slave started"); roken_detach_finish(NULL, daemon_child); restarter_fd = restarter(context, NULL); while (!exit_flag) { struct timeval to; time_t now, elapsed; fd_set readset; int connected = FALSE; #ifndef NO_LIMIT_FD_SETSIZE if (restarter_fd >= FD_SETSIZE) krb5_errx(context, IPROPD_RESTART, "fd too large"); #endif FD_ZERO(&readset); if (restarter_fd > -1) FD_SET(restarter_fd, &readset); now = time(NULL); elapsed = now - before; if (elapsed < reconnect) { time_t left = reconnect - elapsed; krb5_warnx(context, "sleeping %d seconds before " "retrying to connect", (int)left); to.tv_sec = left; to.tv_usec = 0; if (select(restarter_fd + 1, &readset, NULL, NULL, &to) == 1) { exit_flag = SIGTERM; continue; } } before = now; slave_status(context, status_file, "connecting to master: %s\n", master); master_fd = connect_to_master (context, master, port_str); if (master_fd < 0) goto retry; reconnect = reconnect_min; if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; krb5_cc_destroy(context, ccache); get_creds(context, keytab_str, &ccache, master); } if (verbose) krb5_warnx(context, "authenticating to master"); ret = krb5_sendauth (context, &auth_context, &master_fd, IPROP_VERSION, NULL, server, AP_OPTS_MUTUAL_REQUIRED, NULL, NULL, ccache, NULL, NULL, NULL); if (ret) { krb5_warn (context, ret, "krb5_sendauth"); goto retry; } krb5_warnx(context, "ipropd-slave started at version: %ld", (long)server_context->log_context.version); ret = ihave(context, auth_context, master_fd, server_context->log_context.version); if (ret) goto retry; connected = TRUE; if (verbose) krb5_warnx(context, "connected to master"); slave_status(context, status_file, "connected to master, waiting instructions"); while (connected && !exit_flag) { krb5_data out; krb5_storage *sp; uint32_t tmp; int max_fd; #ifndef NO_LIMIT_FD_SETSIZE if (master_fd >= FD_SETSIZE) krb5_errx(context, IPROPD_RESTART, "fd too large"); if (restarter_fd >= FD_SETSIZE) krb5_errx(context, IPROPD_RESTART, "fd too large"); max_fd = max(restarter_fd, master_fd); #endif FD_ZERO(&readset); FD_SET(master_fd, &readset); if (restarter_fd != -1) FD_SET(restarter_fd, &readset); to.tv_sec = time_before_lost; to.tv_usec = 0; ret = select (max_fd + 1, &readset, NULL, NULL, &to); if (ret < 0) { if (errno == EINTR) continue; else krb5_err (context, 1, errno, "select"); } if (ret == 0) { krb5_warnx(context, "server didn't send a message " "in %d seconds", time_before_lost); connected = FALSE; continue; } if (restarter_fd > -1 && FD_ISSET(restarter_fd, &readset)) { if (verbose) krb5_warnx(context, "slave restarter exited"); exit_flag = SIGTERM; } if (!FD_ISSET(master_fd, &readset)) continue; if (verbose) krb5_warnx(context, "message from master"); ret = krb5_read_priv_message(context, auth_context, &master_fd, &out); if (ret) { krb5_warn(context, ret, "krb5_read_priv_message"); connected = FALSE; continue; } sp = krb5_storage_from_mem (out.data, out.length); if (sp == NULL) krb5_err(context, IPROPD_RESTART, errno, "krb5_storage_from_mem"); ret = krb5_ret_uint32(sp, &tmp); if (ret == HEIM_ERR_EOF) { krb5_warn(context, ret, "master sent zero-length message"); connected = FALSE; continue; } if (ret != 0) { krb5_warn(context, ret, "couldn't read master's message"); connected = FALSE; continue; } ret = server_context->db->hdb_open(context, server_context->db, O_RDWR | O_CREAT, 0600); if (ret) krb5_err (context, 1, ret, "db->open while handling a " "message from the master"); ret = kadm5_log_init(server_context); if (ret) { krb5_err(context, IPROPD_RESTART, ret, "kadm5_log_init while " "handling a message from the master"); } ret = server_context->db->hdb_close (context, server_context->db); if (ret) krb5_err (context, 1, ret, "db->close while handling a " "message from the master"); switch (tmp) { case FOR_YOU : if (verbose) krb5_warnx(context, "master sent us diffs"); ret2 = receive(context, sp, server_context); if (ret2) krb5_warn(context, ret2, "receive from ipropd-master had errors"); ret = ihave(context, auth_context, master_fd, server_context->log_context.version); if (ret || ret2) connected = FALSE; /* * If it returns an error, receive() may nonetheless * have committed some entries successfully, so we must * update the slave_status even if there were errors. */ is_up_to_date(context, status_file, server_context); break; case TELL_YOU_EVERYTHING : if (verbose) krb5_warnx(context, "master sent us a full dump"); ret = receive_everything(context, master_fd, server_context, auth_context); if (ret == 0) { ret = ihave(context, auth_context, master_fd, server_context->log_context.version); } if (ret) connected = FALSE; else is_up_to_date(context, status_file, server_context); break; case ARE_YOU_THERE : if (verbose) krb5_warnx(context, "master sent us a ping"); is_up_to_date(context, status_file, server_context); ret = ihave(context, auth_context, master_fd, server_context->log_context.version); if (ret) connected = FALSE; send_im_here(context, master_fd, auth_context); break; case YOU_HAVE_LAST_VERSION: if (verbose) krb5_warnx(context, "master tells us we are up to date"); is_up_to_date(context, status_file, server_context); break; case NOW_YOU_HAVE : case I_HAVE : case ONE_PRINC : case I_AM_HERE : default : krb5_warnx (context, "Ignoring command %d", tmp); break; } krb5_storage_free (sp); krb5_data_free (&out); } slave_status(context, status_file, "disconnected from master"); retry: if (connected == FALSE) krb5_warnx (context, "disconnected for server"); if (exit_flag) krb5_warnx (context, "got an exit signal"); if (master_fd >= 0) close(master_fd); reconnect += backoff; if (reconnect > reconnect_max) { slave_status(context, status_file, "disconnected from master for a long time"); reconnect = reconnect_max; } } if (status_file) { /* XXX It'd be better to leave it saying we're not here */ unlink(status_file); } if (0); #ifndef NO_SIGXCPU else if(exit_flag == SIGXCPU) krb5_warnx(context, "%s CPU time limit exceeded", getprogname()); #endif else if(exit_flag == SIGINT || exit_flag == SIGTERM) krb5_warnx(context, "%s terminated", getprogname()); else krb5_warnx(context, "%s unexpected exit reason: %ld", getprogname(), (long)exit_flag); return 0; }
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; }
/* * pg_krb5_sendauth -- client routine to send authentication information to * the server */ static int pg_krb5_sendauth(char *PQerrormsg, int sock, const char *hostname) { krb5_error_code retval; int ret; krb5_principal server; krb5_auth_context auth_context = NULL; krb5_error *err_ret = NULL; if (!hostname) { snprintf(PQerrormsg, PQERRORMSG_LENGTH, "pg_krb5_sendauth: hostname must be specified for Kerberos authentication\n"); return STATUS_ERROR; } ret = pg_krb5_init(PQerrormsg); if (ret != STATUS_OK) return ret; retval = krb5_sname_to_principal(pg_krb5_context, hostname, PG_KRB_SRVNAM, KRB5_NT_SRV_HST, &server); if (retval) { snprintf(PQerrormsg, PQERRORMSG_LENGTH, "pg_krb5_sendauth: krb5_sname_to_principal: %s\n", error_message(retval)); return STATUS_ERROR; } /* * libpq uses a non-blocking socket. But kerberos needs a blocking * socket, and we have to block somehow to do mutual authentication * anyway. So we temporarily make it blocking. */ if (!pg_set_block(sock)) { char sebuf[256]; snprintf(PQerrormsg, PQERRORMSG_LENGTH, libpq_gettext("could not set socket to blocking mode: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf))); krb5_free_principal(pg_krb5_context, server); return STATUS_ERROR; } retval = krb5_sendauth(pg_krb5_context, &auth_context, (krb5_pointer) & sock, PG_KRB_SRVNAM, pg_krb5_client, server, AP_OPTS_MUTUAL_REQUIRED, NULL, 0, /* no creds, use ccache instead */ pg_krb5_ccache, &err_ret, NULL, NULL); if (retval) { if (retval == KRB5_SENDAUTH_REJECTED && err_ret) { #if defined(HAVE_KRB5_ERROR_TEXT_DATA) snprintf(PQerrormsg, PQERRORMSG_LENGTH, libpq_gettext("Kerberos 5 authentication rejected: %*s\n"), (int) err_ret->text.length, err_ret->text.data); #elif defined(HAVE_KRB5_ERROR_E_DATA) snprintf(PQerrormsg, PQERRORMSG_LENGTH, libpq_gettext("Kerberos 5 authentication rejected: %*s\n"), (int) err_ret->e_data->length, (const char *) err_ret->e_data->data); #else #error "bogus configuration" #endif } else { snprintf(PQerrormsg, PQERRORMSG_LENGTH, "krb5_sendauth: %s\n", error_message(retval)); } if (err_ret) krb5_free_error(pg_krb5_context, err_ret); ret = STATUS_ERROR; } krb5_free_principal(pg_krb5_context, server); if (!pg_set_noblock(sock)) { char sebuf[256]; snprintf(PQerrormsg, PQERRORMSG_LENGTH, libpq_gettext("could not restore non-blocking mode on socket: %s\n"), pqStrerror(errno, sebuf, sizeof(sebuf))); ret = STATUS_ERROR; } return ret; }