-
Notifications
You must be signed in to change notification settings - Fork 314
Open
Description
Postgres supports authentication and optionally encryption using GSSAPI, allowing authentication using Kerberos, SSPI and the like. I'm currently trying to create a PR using https://www.npmjs.com/package/kerberos which is used by mongodb-js, and poking around it seems like it should be possible to implement, though I've run into a bit of a roadblock when it comes to sending raw bytes as required by the GSSAPI authentication process.
WIP code diff
Index: src/connection.js
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/connection.js b/src/connection.js
--- a/src/connection.js (revision 32feb259a3c9abffab761bd1758b3168d9e0cebc)
+++ b/src/connection.js (date 1752946656209)
@@ -11,6 +11,8 @@
import { Query, CLOSE } from './query.js'
import b from './bytes.js'
+import {Kerberos} from "kerberos" // todo: make optional
+
export default Connection
let uid = 1
@@ -18,6 +20,7 @@
const Sync = b().S().end()
, Flush = b().H().end()
, SSLRequest = b().i32(8).i32(80877103).end(8)
+ , GSSRequest = b().i32(8).i32(80877104).end(8)
, ExecuteUnnamed = Buffer.concat([b().E().str(b.N).i32(0).end(), Sync])
, DescribeUnnamed = b().D().str('S').str(b.N).end()
, noop = () => { /* noop */ }
@@ -104,6 +107,8 @@
, nonce = null
, query = null
, final = null
+ , kerberosClient = null
+ , kerberosEncClient = null
const connection = {
queue: queues.closed,
@@ -327,7 +332,6 @@
terminated = false
backendParameters = {}
socket || (socket = await createSocket())
-
if (!socket)
return
@@ -353,7 +357,57 @@
setTimeout(connect, closedDate ? closedDate + delay - performance.now() : 0)
}
- function connected() {
+ async function connectedGss() {
+ let gssToken
+ try {
+ kerberosEncClient = await Kerberos.initializeClient("postgres@" + host)
+ gssToken = await kerberosEncClient.step('')
+ } catch (e) {
+ return false
+ }
+
+ write(GSSRequest)
+ const canGSS = await new Promise(r => socket.once('data', x => r(x[0] === 71))) // G
+ if (!canGSS) {
+ return false
+ }
+ try {
+ while (!kerberosEncClient.contextComplete) {
+ let buffer = Buffer.from(gssToken, 'base64')
+ write(b().raw(buffer).end())
+ let ret = await new Promise(r => socket.once('data', x => r(x)))
+
+ }
+ } catch (e) {
+ console.log(e)
+ return false
+ }
+ const s = StartupMessage()
+ write(s)
+
+ statements = {}
+ needsTypes = options.fetch_types
+ statementId = Math.random().toString(36).slice(2)
+ statementCount = 1
+ lifeTimer.start()
+
+ socket.on('data', data)
+
+ keep_alive && socket.setKeepAlive && socket.setKeepAlive(true, 1000 * keep_alive)
+ }
+
+ async function connected() {
+ if (Kerberos !== null) {
+ try {
+ await connectedGss()
+ } catch (e) {
+ error(e)
+ }
+ }
try {
statements = {}
needsTypes = options.fetch_types
@@ -654,6 +708,8 @@
(
type === 3 ? AuthenticationCleartextPassword :
type === 5 ? AuthenticationMD5Password :
+ Kerberos !== null && type === 7 ? AuthenticationGss :
+ Kerberos !== null && type === 8 ? AuthenticationGssContinue :
type === 10 ? SASL :
type === 11 ? SASLContinue :
type === 12 ? SASLFinal :
@@ -684,6 +740,22 @@
)
}
+ async function AuthenticationGss() {
+ console.assert(Kerberos !== null)
+
+ kerberosClient = await Kerberos.initializeClient("postgres/" + host) //todo
+ b().p().str('GSSAPI' + b.N)
+ const i = b.i
+ write(b.inc(1).str('p=' + kerberosClient.response).i32(b.i - i - 1, i).end())
+ }
+
+ async function AuthenticationGssContinue(x) {
+ console.assert(Kerberos !== null)
+ console.assert(kerberosClient !== null)
+
+
+ }
+
async function SASL() {
nonce = (await crypto.randomBytes(18)).toString('base64')
b().p().str('SCRAM-SHA-256' + b.N)
The above code fails at write(b().raw(buffer).end())
, and I'm not sure if returning data using ret = await new Promise(r => socket.once('data', x => r(x)))
is correct.
ref:
Metadata
Metadata
Assignees
Labels
No labels