/* * pg_krb4_sendauth -- client routine to send authentication information to * the server * * This routine does not do mutual authentication, nor does it return enough * information to do encrypted connections. But then, if we want to do * encrypted connections, we'll have to redesign the whole RPC mechanism * anyway. * * If the user is too lazy to feed us a hostname, we try to come up with * something other than "localhost" since the hostname is used as an * instance and instance names in v4 databases are usually actual hostnames * (canonicalized to omit all domain suffixes). */ static int pg_krb4_sendauth(char *PQerrormsg, int sock, struct sockaddr_in * laddr, struct sockaddr_in * raddr, const char *hostname) { long krbopts = 0; /* one-way authentication */ KTEXT_ST clttkt; int status; char hostbuf[MAXHOSTNAMELEN]; const char *realm = getenv("PGREALM"); /* NULL == current realm */ if (!hostname || !(*hostname)) { if (gethostname(hostbuf, MAXHOSTNAMELEN) < 0) strcpy(hostbuf, "localhost"); hostname = hostbuf; } pg_krb4_init(); status = krb_sendauth(krbopts, sock, &clttkt, PG_KRB_SRVNAM, hostname, realm, (u_long) 0, NULL, NULL, NULL, laddr, raddr, PG_KRB4_VERSION); if (status != KSUCCESS) { snprintf(PQerrormsg, PQERRORMSG_LENGTH, libpq_gettext("Kerberos 4 error: %s\n"), krb_err_txt[status]); return STATUS_ERROR; } return STATUS_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 */
/* This function has not been changed to deal with NO_SOCKET_TO_FD (i.e., systems on which sockets cannot be converted to file descriptors). The first person to try building a kerberos client on such a system (OS/2, Windows 95, and maybe others) will have to take care of this. */ void start_kerberos4_server (cvsroot_t *root, struct buffer **to_server_p, struct buffer **from_server_p) { int s; int port; struct hostent *hp; struct sockaddr_in sin; char *hname; s = socket (AF_INET, SOCK_STREAM, 0); if (s < 0) error (1, 0, "cannot create socket: %s", SOCK_STRERROR (SOCK_ERRNO)); port = get_cvs_port_number (root); hp = init_sockaddr (&sin, root->hostname, port); hname = xstrdup (hp->h_name); TRACE (TRACE_FUNCTION, "Connecting to %s(%s):%d", root->hostname, inet_ntoa (sin.sin_addr), port); if (connect (s, (struct sockaddr *) &sin, sizeof sin) < 0) error (1, 0, "connect to %s(%s):%d failed: %s", root->hostname, inet_ntoa (sin.sin_addr), port, SOCK_STRERROR (SOCK_ERRNO)); { const char *realm; struct sockaddr_in laddr; int laddrlen; KTEXT_ST ticket; MSG_DAT msg_data; CREDENTIALS cred; int status; realm = krb_realmofhost (hname); laddrlen = sizeof (laddr); if (getsockname (s, (struct sockaddr *) &laddr, &laddrlen) < 0) error (1, 0, "getsockname failed: %s", SOCK_STRERROR (SOCK_ERRNO)); /* We don't care about the checksum, and pass it as zero. */ status = krb_sendauth (KOPT_DO_MUTUAL, s, &ticket, "rcmd", hname, realm, (unsigned long) 0, &msg_data, &cred, sched, &laddr, &sin, "KCVSV1.0"); if (status != KSUCCESS) error (1, 0, "kerberos authentication failed: %s", krb_get_err_text (status)); memcpy (kblock, cred.session, sizeof (C_Block)); } close_on_exec (s); free (hname); /* Give caller the values it wants. */ make_bufs_from_fds (s, s, 0, root, to_server_p, from_server_p, 1); }
/* * The rcmd call itself. * ahost (IN) remote hostname * addr (IN) IP address * locuser (IN) local username * remuser (IN) remote username * cmd (IN) command to execute * rank (IN) MPI rank * fd2p (IN/OUT) if non-NULL, open stderr backchannel on this fd * s (RETURN) socket for stdout/sdin or -1 on failure */ static int k4cmd(char *ahost, char *addr, char *locuser, char *remuser, char *cmd, int rank, int *fd2p, void **arg) { KTEXT_ST ticket; /* kerberos IV context */ CREDENTIALS cred; Key_schedule schedule; MSG_DAT msg_data; struct sockaddr_in faddr; struct sockaddr_in laddr; int s, pid; sigset_t oldset, blockme; struct sockaddr_in sin, from; char c; int lport = IPPORT_RESERVED - 1; unsigned long krb_options = 0L; static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER; int status; int rc, rv; struct xpollfd xpfds[2]; pid = getpid(); sigemptyset(&blockme); sigaddset(&blockme, SIGURG); pthread_sigmask(SIG_BLOCK, &blockme, &oldset); for (;;) { s = privsep_rresvport(&lport); if (s < 0) { if (errno == EAGAIN) err("%p: %S: socket: All ports in use\n", ahost); else err("%p: %S: k4cmd: socket: %m\n", ahost); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); } fcntl(s, F_SETOWN, pid); sin.sin_family = AF_INET; memcpy((caddr_t) & sin.sin_addr, addr, IP_ADDR_LEN); sin.sin_port = htons(KCMD_PORT); rv = connect(s, (struct sockaddr *) &sin, sizeof(sin)); if (rv >= 0) break; (void) close(s); if (errno == EADDRINUSE) { lport--; continue; } if (errno == EINTR) err("%p: %S: connect timed out\n", ahost); else err("%p: %S: %m\n", ahost); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); } if (fd2p == 0) { write(s, "", 1); lport = 0; } else { char num[8]; int s2, s3; socklen_t len = sizeof(from); s2 = privsep_rresvport(&lport); if (s2 < 0) { goto bad; } listen(s2, 1); (void) snprintf(num, sizeof(num), "%d", lport); if (write(s, num, strlen(num) + 1) != strlen(num) + 1) { err("%p: %S: write: setting up stderr: %m\n", ahost); (void) close(s2); goto bad; } errno = 0; xpfds[0].fd = s; xpfds[1].fd = s2; xpfds[0].events = xpfds[1].events = XPOLLREAD; if (((rv = xpoll(xpfds, 2, -1)) < 0) || rv != 1 || (xpfds[0].revents > 0)) { if (errno != 0) err("%p: %S: k4cmd: xpoll (setting up stderr): %m\n", ahost); else err("%p: %S: k4cmd: xpoll: protocol failure in circuit setup\n", ahost); (void) close(s2); goto bad; } s3 = accept(s2, (struct sockaddr *) &from, &len); (void) close(s2); if (s3 < 0) { err("%p: %S: accept: %m\n", ahost); lport = 0; goto bad; } *fd2p = s3; from.sin_port = ntohs((u_short) from.sin_port); } /* * Kerberos-authenticated service. Don't have to send locuser, since * its already in the ticket, and we'll extract it on the other side. */ /*krb_options |= KOPT_DONT_CANON; */ pthread_mutex_lock(&mylock); status = krb_sendauth(krb_options, s, &ticket, "rcmd", ahost, NULL, (unsigned long) pid, &msg_data, &cred, schedule, &laddr, &faddr, "KCMDV0.1"); if (status != KSUCCESS) { /* * this part involves some very intimate knowledge of a * particular sendauth implementation to pry out the old * bits. This only catches the case of total failure -- but * that's the one where we get useful data from the remote * end. If we even get an authenticator back, then the * problem gets diagnosed locally anyhow. */ extern KRB_INT32 __krb_sendauth_hidden_tkt_len; char *old_data = (char *) &__krb_sendauth_hidden_tkt_len; char tmpbuf[LINEBUFSIZE]; char *p = tmpbuf; if ((status == KFAILURE) && (*old_data == 1)) { strncpy(tmpbuf, old_data + 1, 3); tmpbuf[3] = '\0'; err("%p: %S: %s", ahost, tmpbuf); *old_data = (-1); } if ((status == KFAILURE) && (*old_data == (char) -1)) { while (read(s, &c, 1) == 1) { /*(void) write(2, &c, 1); */ *p++ = c; if (c == '\n') break; } *p++ = '\0'; err("%p: %S: %s", ahost, tmpbuf); status = -1; } switch (status) { case KDC_PR_UNKNOWN: err("%p: %S: not registered for kerberos\n", ahost); break; case NO_TKT_FIL: err("%p: %S: no tickets file found\n", ahost); break; default: err("%p: %S: k4cmd failed: %s\n", ahost, (status == -1) ? "k4cmd protocol failure" : krb_get_err_text(status)); } pthread_mutex_unlock(&mylock); goto bad2; } pthread_mutex_unlock(&mylock); (void) write(s, remuser, strlen(remuser) + 1); (void) write(s, cmd, strlen(cmd) + 1); if ((rc = read(s, &c, 1)) != 1) { if (rc == -1) { err("%p: %S: read: %m\n", ahost); } else { err("%p: %S: k4cmd: bad connection with remote host\n", ahost); } goto bad2; } if (c != 0) { /* retrieve error string from remote server */ char tmpbuf[LINEBUFSIZE]; char *p = tmpbuf; while (read(s, &c, 1) == 1) { *p++ = c; if (c == '\n') break; } if (c != '\n') *p++ = '\n'; *p++ = '\0'; err("%S: %s", ahost, tmpbuf); goto bad2; } pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (s); bad2: if (lport) (void) close(*fd2p); bad: (void) close(s); pthread_sigmask(SIG_SETMASK, &oldset, NULL); return (-1); }