示例#1
0
/*
 * 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
}
示例#2
0
/*
 * 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
}
示例#3
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;
}
示例#4
0
/*
 * 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;
}
示例#5
0
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;
}
示例#6
0
/*
 * 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;
}
示例#7
0
/*
 *	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;
}
示例#8
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;
}