/* * Respond to AUTH_REQ_SCM_CREDS challenge. * * Note: current backends will not use this challenge if HAVE_GETPEEREID * or SO_PEERCRED is defined, but pre-7.4 backends might, so compile the * code anyway. */ static int pg_local_sendauth(PGconn *conn) { #if defined(HAVE_STRUCT_CMSGCRED) || defined(HAVE_STRUCT_FCRED) || \ (defined(HAVE_STRUCT_SOCKCRED) && defined(LOCAL_CREDS)) char buf; struct iovec iov; struct msghdr msg; #ifdef HAVE_STRUCT_CMSGCRED /* Prevent padding */ char cmsgmem[sizeof(struct cmsghdr) + sizeof(struct cmsgcred)]; /* Point to start of first structure */ struct cmsghdr *cmsg = (struct cmsghdr *) cmsgmem; #endif /* * The backend doesn't care what we send here, but it wants exactly one * character to force recvmsg() to block and wait for us. */ buf = '\0'; iov.iov_base = &buf; iov.iov_len = 1; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; #ifdef HAVE_STRUCT_CMSGCRED /* Create control header, FreeBSD */ msg.msg_control = cmsg; msg.msg_controllen = sizeof(cmsgmem); memset(cmsg, 0, sizeof(cmsgmem)); cmsg->cmsg_len = sizeof(cmsgmem); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDS; #endif if (sendmsg(conn->sock, &msg, 0) == -1) { char sebuf[256]; printfPQExpBuffer(&conn->errorMessage, "pg_local_sendauth: sendmsg: %s\n", pqStrerror(errno, sebuf, sizeof(sebuf))); return STATUS_ERROR; } return STATUS_OK; #else printfPQExpBuffer(&conn->errorMessage, libpq_gettext("SCM_CRED authentication method not supported\n")); return STATUS_ERROR; #endif }
/* * Respond to AUTH_REQ_SCM_CREDS challenge. * * Note: this is dead code as of Postgres 9.1, because current backends will * never send this challenge. But we must keep it as long as libpq needs to * interoperate with pre-9.1 servers. It is believed to be needed only on * Debian/kFreeBSD (ie, FreeBSD kernel with Linux userland, so that the * getpeereid() function isn't provided by libc). */ static int pg_local_sendauth(PGconn *conn) { #ifdef HAVE_STRUCT_CMSGCRED char buf; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; unsigned char buf[CMSG_SPACE(sizeof(struct cmsgcred))]; } cmsgbuf; /* * The backend doesn't care what we send here, but it wants exactly one * character to force recvmsg() to block and wait for us. */ buf = '\0'; iov.iov_base = &buf; iov.iov_len = 1; memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; /* We must set up a message that will be filled in by kernel */ memset(&cmsgbuf, 0, sizeof(cmsgbuf)); msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_len = CMSG_LEN(sizeof(struct cmsgcred)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDS; if (sendmsg(conn->sock, &msg, 0) == -1) { char sebuf[256]; printfPQExpBuffer(&conn->errorMessage, "pg_local_sendauth: sendmsg: %s\n", pqStrerror(errno, sebuf, sizeof(sebuf))); return STATUS_ERROR; } return STATUS_OK; #else printfPQExpBuffer(&conn->errorMessage, libpq_gettext("SCM_CRED authentication method not supported\n")); return STATUS_ERROR; #endif }
/* * 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; }
/* * lo_export - * exports an (inversion) large object. * returns -1 upon failure, 1 if OK */ int lo_export(PGconn *conn, Oid lobjId, const char *filename) { int result = 1; int fd; int nbytes, tmp; char buf[LO_BUFSIZE]; int lobj; char sebuf[256]; /* * open the large object. */ lobj = lo_open(conn, lobjId, INV_READ); if (lobj == -1) { /* we assume lo_open() already set a suitable error message */ return -1; } /* * create the file to be written to */ fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666); if (fd < 0) { /* error */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not open file \"%s\": %s\n"), filename, pqStrerror(errno, sebuf, sizeof(sebuf))); (void) lo_close(conn, lobj); return -1; } /* * read in from the large object and write to the file */ while ((nbytes = lo_read(conn, lobj, buf, LO_BUFSIZE)) > 0) { tmp = write(fd, buf, nbytes); if (tmp != nbytes) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not write to file \"%s\": %s\n"), filename, pqStrerror(errno, sebuf, sizeof(sebuf))); (void) lo_close(conn, lobj); (void) close(fd); return -1; } } /* * If lo_read() failed, we are now in an aborted transaction so there's no * need for lo_close(); furthermore, if we tried it we'd overwrite the * useful error result with a useless one. So skip lo_close() if we got a * failure result. */ if (nbytes < 0 || lo_close(conn, lobj) != 0) { /* assume lo_read() or lo_close() left a suitable error message */ result = -1; } if (close(fd)) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not write to file \"%s\": %s\n"), filename, pqStrerror(errno, sebuf, sizeof(sebuf))); result = -1; } return result; }
static Oid lo_import_internal(PGconn *conn, const char *filename, const Oid oid) { int fd; int nbytes, tmp; char buf[LO_BUFSIZE]; Oid lobjOid; int lobj; char sebuf[256]; /* * open the file to be read in */ fd = open(filename, O_RDONLY | PG_BINARY, 0666); if (fd < 0) { /* error */ printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not open file \"%s\": %s\n"), filename, pqStrerror(errno, sebuf, sizeof(sebuf))); return InvalidOid; } /* * create an inversion object */ if (oid == InvalidOid) lobjOid = lo_creat(conn, INV_READ | INV_WRITE); else lobjOid = lo_create(conn, oid); if (lobjOid == InvalidOid) { /* we assume lo_create() already set a suitable error message */ (void) close(fd); return InvalidOid; } lobj = lo_open(conn, lobjOid, INV_WRITE); if (lobj == -1) { /* we assume lo_open() already set a suitable error message */ (void) close(fd); return InvalidOid; } /* * read in from the file and write to the large object */ while ((nbytes = read(fd, buf, LO_BUFSIZE)) > 0) { tmp = lo_write(conn, lobj, buf, nbytes); if (tmp != nbytes) { /* * If lo_write() failed, we are now in an aborted transaction so * there's no need for lo_close(); furthermore, if we tried it * we'd overwrite the useful error result with a useless one. So * just nail the doors shut and get out of town. */ (void) close(fd); return InvalidOid; } } if (nbytes < 0) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not read from file \"%s\": %s\n"), filename, pqStrerror(errno, sebuf, sizeof(sebuf))); lobjOid = InvalidOid; } (void) close(fd); if (lo_close(conn, lobj) != 0) { /* we assume lo_close() already set a suitable error message */ return InvalidOid; } return lobjOid; }
/* * pg_fe_getauthname * * Returns a pointer to malloc'd space containing whatever name the user * has authenticated to the system. If there is an error, return NULL, * and put a suitable error message in *errorMessage if that's not NULL. */ char * pg_fe_getauthname(PQExpBuffer errorMessage) { char *result = NULL; const char *name = NULL; #ifdef WIN32 /* Microsoft recommends buffer size of UNLEN+1, where UNLEN = 256 */ char username[256 + 1]; DWORD namesize = sizeof(username); #else uid_t user_id = geteuid(); char pwdbuf[BUFSIZ]; struct passwd pwdstr; struct passwd *pw = NULL; int pwerr; #endif /* * Some users are using configure --enable-thread-safety-force, so we * might as well do the locking within our library to protect * pqGetpwuid(). In fact, application developers can use getpwuid() in * their application if they use the locking call we provide, or install * their own locking function using PQregisterThreadLock(). */ pglock_thread(); #ifdef WIN32 if (GetUserName(username, &namesize)) name = username; else if (errorMessage) printfPQExpBuffer(errorMessage, libpq_gettext("user name lookup failure: error code %lu\n"), GetLastError()); #else pwerr = pqGetpwuid(user_id, &pwdstr, pwdbuf, sizeof(pwdbuf), &pw); if (pw != NULL) name = pw->pw_name; else if (errorMessage) { if (pwerr != 0) printfPQExpBuffer(errorMessage, libpq_gettext("could not look up local user ID %d: %s\n"), (int) user_id, pqStrerror(pwerr, pwdbuf, sizeof(pwdbuf))); else printfPQExpBuffer(errorMessage, libpq_gettext("local user with ID %d does not exist\n"), (int) user_id); } #endif if (name) { result = strdup(name); if (result == NULL && errorMessage) printfPQExpBuffer(errorMessage, libpq_gettext("out of memory\n")); } pgunlock_thread(); return result; }
/* * Callback used by SSL to load client cert and key. * This callback is only called when the server wants a * client cert. * * Must return 1 on success, 0 on no data or error. */ static int client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey) { char homedir[MAXPGPATH]; struct stat buf; #ifndef WIN32 struct stat buf2; #endif char fnbuf[MAXPGPATH]; FILE *fp; PGconn *conn = (PGconn *) SSL_get_app_data(ssl); int (*cb) () = NULL; /* how to read user password */ char sebuf[256]; if (!pqGetHomeDirectory(homedir, sizeof(homedir))) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not get user information\n")); return 0; } /* read the user certificate */ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USERCERTFILE); if ((fp = fopen(fnbuf, "r")) == NULL) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not open certificate file \"%s\": %s\n"), fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf))); return 0; } if (PEM_read_X509(fp, x509, NULL, NULL) == NULL) { char *err = SSLerrmessage(); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not read certificate file \"%s\": %s\n"), fnbuf, err); SSLerrfree(err); fclose(fp); return 0; } fclose(fp); /* read the user key */ snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USERKEYFILE); if (stat(fnbuf, &buf) == -1) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("certificate present, but not private key file \"%s\"\n"), fnbuf); return 0; } #ifndef WIN32 if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) || buf.st_uid != geteuid()) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("private key file \"%s\" has wrong permissions\n"), fnbuf); return 0; } #endif if ((fp = fopen(fnbuf, "r")) == NULL) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not open private key file \"%s\": %s\n"), fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf))); return 0; } #ifndef WIN32 if (fstat(fileno(fp), &buf2) == -1 || buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("private key file \"%s\" changed during execution\n"), fnbuf); return 0; } #endif if (PEM_read_PrivateKey(fp, pkey, cb, NULL) == NULL) { char *err = SSLerrmessage(); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("could not read private key file \"%s\": %s\n"), fnbuf, err); SSLerrfree(err); fclose(fp); return 0; } fclose(fp); /* verify that the cert and key go together */ if (!X509_check_private_key(*x509, *pkey)) { char *err = SSLerrmessage(); printfPQExpBuffer(&conn->errorMessage, libpq_gettext("certificate does not match private key file \"%s\": %s\n"), fnbuf, err); SSLerrfree(err); return 0; } return 1; }
/* * 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; }