-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pgwire: Add dynamic user identity mapping
This commit adds support for mapping incoming system identities (e.g.: GSSAPI or X.509 principals) to database usernames. The implementation follows in the same pattern as the HBA configuration. Namely, there is a new cluster setting server.identity_map.configuration which contains data compatible with the pg_ident.conf file. When a new connection is made, the relevant HBA configuration line is selected and the "map" option is used to select an identity-map ruleset. The system identity is mapped to a database username and authentication proceeds using the HBA entry method. CockroachDB explicitly disallows the use of the "map" entry with the "password" HBA method. When using the "cert-password" method, an identity mapping is applied only for incoming connections that present a client certificate; password-based connections will not be subject to any identity remapping. The root user is never subject to identity remapping and cannot be locked out by a bad identity-map configuration. The effective HBA configuration always has a system-defined rule that matches the root user as the first rule. This baked-in rule does not contain a "map" option, thus bypassing the mapper code entirely. Users must already have access to the ADMIN role in order to change cluster settings, so this change does not, in and of itself, present privilege-escalation concerns. Futhermore, if the identity mapping results in the "root", "node", or other reserved usernames, an error will be returned to the client. For pedantry's sake, the term "identity" is preferred over "username" within the code, since not all identities are necessarily what would be considered usernames. Fixes: #47196 See also: https://www.postgresql.org/docs/13/auth-username-maps.html Release note (security update): The server.identity_map.configuration cluster setting allows a pg_ident.conf file to be uploaded to support dynamically remapping system identities (e.g.: Kerberos or X.509 principals) to database usernames. This supports use-cases where X.509 certificates must conform to organizational standards that mandate the use of Common Names that are not valid SQL usernames (e.g.: CN=carl@example.com => carl). Mapping rules that result in the root, node, or other reserved usernames will result in an error when the client attempts to connect. Release note (security update): The client_authentication_info structured log message provides a new "SystemIdentity" field with the client-provided system identity. The existing "User" field will be populated after any Host-Based Authentication rules have been selected and applied, which may include a system-identity to database-username mapping. Release note (security update): GSSAPI-based authentication can now use either the HBA "map" option or "include_realm=0" to map the incoming princpal to a database username. Existing configurations will operate unchanged, however operators are encouraged to migrate from "include_realm=0" to "map" to avoid ambiguity in deployments where multiple realms are present. Release note (security update): Incoming system identities are normalized to lower-case before they are evaluated against any active identity-mapping HBA configuration. For example, an incoming GSSAPI principal "carl@EXAMPLE.COM" would only be matched by rules such as "example carl@example.com carl" or "example /^(.*)@example.com$ \1".
- Loading branch information
Showing
29 changed files
with
1,691 additions
and
369 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
// Copyright 2021 The Cockroach Authors. | ||
// | ||
// Licensed as a CockroachDB Enterprise file under the Cockroach Community | ||
// License (the "License"); you may not use this file except in compliance with | ||
// the License. You may obtain a copy of the License at | ||
// | ||
// https://github.com/cockroachdb/cockroach/blob/master/licenses/CCL.txt | ||
|
||
// See comment on build tag in gssapi.go. | ||
|
||
//go:build gss | ||
// +build gss | ||
|
||
package gssapiccl | ||
|
||
// This file contains the code that calls out to the GSSAPI library | ||
// to retrieve the current user. | ||
|
||
import ( | ||
"unsafe" | ||
|
||
"github.com/cockroachdb/cockroach/pkg/sql/pgwire" | ||
"github.com/cockroachdb/errors" | ||
) | ||
|
||
// #cgo LDFLAGS: -lgssapi_krb5 -lcom_err -lkrb5 -lkrb5support -ldl -lk5crypto -lresolv | ||
// | ||
// #include <gssapi/gssapi.h> | ||
// #include <stdlib.h> | ||
import "C" | ||
|
||
func getGssUser(c pgwire.AuthConn) (connClose func(), gssUser string, _ error) { | ||
var ( | ||
majStat, minStat, lminS, gflags C.OM_uint32 | ||
gbuf C.gss_buffer_desc | ||
contextHandle C.gss_ctx_id_t = C.GSS_C_NO_CONTEXT | ||
acceptorCredHandle C.gss_cred_id_t = C.GSS_C_NO_CREDENTIAL | ||
srcName C.gss_name_t | ||
outputToken C.gss_buffer_desc | ||
) | ||
|
||
if err := c.SendAuthRequest(authTypeGSS, nil); err != nil { | ||
return nil, "", err | ||
} | ||
|
||
// This cleanup function must be called at the | ||
// "completion of a communications session", not | ||
// merely at the end of an authentication init. See | ||
// https://tools.ietf.org/html/rfc2744.html, section | ||
// `1. Introduction`, stage `d`: | ||
// | ||
// At the completion of a communications session (which | ||
// may extend across several transport connections), | ||
// each application calls a GSS-API routine to delete | ||
// the security context. | ||
// | ||
// See https://github.com/postgres/postgres/blob/f4d59369d2ddf0ad7850112752ec42fd115825d4/src/backend/libpq/pqcomm.c#L269 | ||
connClose = func() { | ||
C.gss_delete_sec_context(&lminS, &contextHandle, C.GSS_C_NO_BUFFER) | ||
} | ||
|
||
for { | ||
token, err := c.GetPwdData() | ||
if err != nil { | ||
return connClose, "", err | ||
} | ||
|
||
gbuf.length = C.ulong(len(token)) | ||
gbuf.value = C.CBytes([]byte(token)) | ||
|
||
majStat = C.gss_accept_sec_context( | ||
&minStat, | ||
&contextHandle, | ||
acceptorCredHandle, | ||
&gbuf, | ||
C.GSS_C_NO_CHANNEL_BINDINGS, | ||
&srcName, | ||
nil, | ||
&outputToken, | ||
&gflags, | ||
nil, | ||
nil, | ||
) | ||
C.free(unsafe.Pointer(gbuf.value)) | ||
|
||
if outputToken.length != 0 { | ||
outputBytes := C.GoBytes(outputToken.value, C.int(outputToken.length)) | ||
C.gss_release_buffer(&lminS, &outputToken) | ||
if err := c.SendAuthRequest(authTypeGSSContinue, outputBytes); err != nil { | ||
return connClose, "", err | ||
} | ||
} | ||
if majStat != C.GSS_S_COMPLETE && majStat != C.GSS_S_CONTINUE_NEEDED { | ||
return connClose, "", gssError("accepting GSS security context failed", majStat, minStat) | ||
} | ||
if majStat != C.GSS_S_CONTINUE_NEEDED { | ||
break | ||
} | ||
} | ||
|
||
majStat = C.gss_display_name(&minStat, srcName, &gbuf, nil) | ||
if majStat != C.GSS_S_COMPLETE { | ||
return connClose, "", gssError("retrieving GSS user name failed", majStat, minStat) | ||
} | ||
gssUser = C.GoStringN((*C.char)(gbuf.value), C.int(gbuf.length)) | ||
C.gss_release_buffer(&lminS, &gbuf) | ||
|
||
return connClose, gssUser, nil | ||
} | ||
|
||
func gssError(msg string, majStat, minStat C.OM_uint32) error { | ||
var ( | ||
gmsg C.gss_buffer_desc | ||
lminS, msgCtx C.OM_uint32 | ||
) | ||
|
||
msgCtx = 0 | ||
C.gss_display_status(&lminS, majStat, C.GSS_C_GSS_CODE, C.GSS_C_NO_OID, &msgCtx, &gmsg) | ||
msgMajor := C.GoString((*C.char)(gmsg.value)) | ||
C.gss_release_buffer(&lminS, &gmsg) | ||
|
||
msgCtx = 0 | ||
C.gss_display_status(&lminS, minStat, C.GSS_C_MECH_CODE, C.GSS_C_NO_OID, &msgCtx, &gmsg) | ||
msgMinor := C.GoString((*C.char)(gmsg.value)) | ||
C.gss_release_buffer(&lminS, &gmsg) | ||
|
||
return errors.Errorf("%s: %s: %s", msg, msgMajor, msgMinor) | ||
} |
Oops, something went wrong.