Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -4917,6 +4917,7 @@ if BUILD_OIDC_CHILD
oidc_child_SOURCES = \
src/oidc_child/oidc_child.c \
src/oidc_child/oidc_child_curl.c \
src/oidc_child/oidc_child_gssapi.c \
src/oidc_child/oidc_child_json.c \
src/oidc_child/oidc_child_id.c \
src/oidc_child/libcrypto/oidc_child_get_jwk.c \
Expand All @@ -4937,6 +4938,7 @@ oidc_child_CFLAGS = \
$(CURL_CFLAGS) \
$(UUID_CFLAGS) \
$(CRYPTO_CFLAGS) \
$(GSSAPI_KRB5_CFLAGS) \
$(NULL)
oidc_child_LDADD = \
libsss_debug.la \
Expand All @@ -4947,6 +4949,7 @@ oidc_child_LDADD = \
$(CURL_LIBS) \
$(UUID_LIBS) \
$(CRYPTO_LIBS) \
$(GSSAPI_KRB5_LIBS) \
$(NULL)
endif

Expand Down
5 changes: 4 additions & 1 deletion contrib/sssd.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -539,7 +539,10 @@ unset CK_TIMEOUT_MULTIPLIER

%install

%make_install
# Serial install: libtool relinks sssdlib modules against pkglib
# libraries during install. Parallel install can relink before the
# target libraries are in place, causing missing .so files.
%make_install -j1

# Prepare language files
/usr/lib/rpm/find-lang.sh $RPM_BUILD_ROOT sssd
Expand Down
165 changes: 145 additions & 20 deletions src/man/sssd-idp.5.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,51 @@
<listitem>
<para>
Required option that specifies the IdP product.
Currently Entra ID (entra_id) and Keycloak
(keycloak) are supported.
Currently Entra ID (<quote>entra_id</quote>),
Keycloak (<quote>keycloak</quote>), and the
FreeIPA integrated IdP (<quote>ahdapa</quote>)
are supported.
</para>
<para>
Depending on the IdP product additional platform
specific options might follow the name separated
by a colon (:). E.g. for Keycloak the base URI for
the user and group REST API must be given. For
Entra ID the base URI for the Microsoft Graph API
can be given to use sovereign or government cloud
endpoints instead of the default
(https://graph.microsoft.com/v1.0). E.g.
<quote>entra_id:https://graph.microsoft.us/v1.0</quote>
for the US government cloud (GCC High).
Depending on the IdP product, additional
platform-specific options may follow the name
separated by a colon (:).
</para>
<para>
For <emphasis role="bold">Keycloak</emphasis>, the
base URI for the user and group REST API must be
given, e.g.
<quote>keycloak:https://master.keycloak.test:8443/auth/admin/realms/master/</quote>.
</para>
<para>
For <emphasis role="bold">Entra ID</emphasis>, the
base URI for the Microsoft Graph API can be given to
use sovereign or government cloud endpoints instead
of the default
(<literal>https://graph.microsoft.com/v1.0</literal>),
e.g.
<quote>entra_id:https://graph.microsoft.us/v1.0</quote>
for the US government cloud (GCC High).
</para>
<para>
For the <emphasis role="bold">FreeIPA integrated
IdP (ahdapa)</emphasis>, the base URL of the
ahdapa server must be given. SSSD will
automatically derive the token endpoint
(<literal>&lt;base&gt;/token</literal>), the device
authorization endpoint
(<literal>&lt;base&gt;/device_authorization</literal>),
and the userinfo endpoint
(<literal>&lt;base&gt;/userinfo</literal>) from this
base URL when the corresponding explicit options are
not set. Explicit values for those options take
precedence if provided.
</para>
<para>
In a standard FreeIPA deployment ahdapa is mounted
at the <literal>/idp</literal> path of the IPA
server, so the value would be
<quote>ahdapa:https://server.example.com/idp</quote>.
</para>
<para>
Default: Not set (Required)
Expand All @@ -103,10 +134,13 @@
<term>idp_client_secret (string)</term>
<listitem>
<para>
Password of the IdP client. The password is
required for the id_provider. If only used as
Password of the IdP client. Required for the
id_provider unless
<quote>idp_client_use_gssapi = true</quote> is set,
in which case GSSAPI/Kerberos is used instead and
no client secret is needed. If only used as
auth_provider it depends on the server side
configuration if it is required or not.
configuration whether it is required or not.
</para>
<para>
Default: Not set
Expand All @@ -118,9 +152,13 @@
<listitem>
<para>
IdP endpoint for requesting access tokens.
When <quote>idp_type</quote> is set to
<quote>ahdapa:&lt;base&gt;</quote> and this
option is not set, it defaults to
<literal>&lt;base&gt;/token</literal>.
</para>
<para>
Default: Not set (Required)
Default: Not set (Required unless auto-derived)
</para>
</listitem>
</varlistentry>
Expand All @@ -130,10 +168,13 @@
<para>
IdP endpoint for device authorization according
to RFC-8628. This is required for user
authentication.
authentication. When <quote>idp_type</quote> is
set to <quote>ahdapa:&lt;base&gt;</quote> and
this option is not set, it defaults to
<literal>&lt;base&gt;/device_authorization</literal>.
</para>
<para>
Default: Not set
Default: Not set (Required unless auto-derived)
</para>
</listitem>
</varlistentry>
Expand All @@ -143,10 +184,14 @@
<para>
IdP userinfo endpoint to request user attributes
after a successful authentication of the user.
Required for authentication.
Required for authentication. When
<quote>idp_type</quote> is set to
<quote>ahdapa:&lt;base&gt;</quote> and this
option is not set, it defaults to
<literal>&lt;base&gt;/userinfo</literal>.
</para>
<para>
Default: Not set
Default: Not set (Required unless auto-derived)
</para>
</listitem>
</varlistentry>
Expand Down Expand Up @@ -217,6 +262,68 @@
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>idp_client_use_gssapi (boolean)</term>
<listitem>
<para>
When set to <quote>true</quote>, SSSD authenticates
to the IdP token and device authorization endpoints
using GSSAPI/Kerberos (HTTP Negotiate /
SPNEGO) instead of a static client secret.
The host Kerberos keytab is used to acquire
credentials for the machine's
<literal>host/hostname@REALM</literal> principal,
which are stored in a per-invocation
<literal>MEMORY:</literal> credential cache.
libcurl then presents a Negotiate token on the
token endpoint; the server verifies the AP-REQ and
issues an access token.
</para>
<para>
This option is designed for FreeIPA-enrolled
machines where a single template OAuth2 client is
registered on the IdP with
<literal>token_endpoint_auth_method = kerberos_client_auth</literal>
and a principal glob such as
<literal>host/*@REALM</literal>. No per-machine
<quote>idp_client_secret</quote> needs to be
distributed or rotated.
</para>
<para>
When <quote>idp_client_use_gssapi = true</quote>,
<quote>idp_client_secret</quote> must not be set.
GSSAPI is used only at the token and device
authorization endpoints; all identity API calls use
a Bearer token obtained from the token endpoint.
</para>
<para>
Default: false
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>idp_client_keytab (string)</term>
<listitem>
<para>
Path to the Kerberos keytab used to acquire
credentials when
<quote>idp_client_use_gssapi = true</quote>.
The keytab must contain the host principal
(<literal>host/hostname@REALM</literal>) and must
be readable by the sssd process.
</para>
<para>
Because <filename>/etc/krb5.keytab</filename> is
typically readable only by root, a keytab copy with
appropriate permissions should be created and
referenced here (similar to the approach used for
IPA trust keytabs).
</para>
<para>
Default: <filename>/etc/krb5.keytab</filename>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>idmap_range_min (integer)</term>
<listitem>
Expand Down Expand Up @@ -294,6 +401,24 @@ idp_userinfo_endpoint = https://master.keycloak.test:8443/auth/realms/master/pro
idp_device_auth_endpoint = https://master.keycloak.test:8443/auth/realms/master/protocol/openid-connect/auth/device
idp_id_scope = profile
idp_auth_scope = openid profile email
</programlisting>
<programlisting>
[domain/ahdapa]
id_provider = idp
auth_provider = idp

; idp_type sets the base URL; token, device-auth, and userinfo endpoints
; are derived automatically as &lt;base&gt;/token, /device_authorization, /userinfo
idp_type = ahdapa:https://idp.example.com

idp_client_id = &lt;client_id of the template kerberos_client_auth client&gt;
; Use the host Kerberos keytab instead of a static client secret.
; The keytab must be readable by the sssd process.
idp_client_use_gssapi = true
idp_client_keytab = /etc/sssd/host.keytab

idp_id_scope = openid directory.read
idp_auth_scope = openid
</programlisting>
</para>
</refsect1>
Expand Down
33 changes: 31 additions & 2 deletions src/oidc_child/oidc_child.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ static errno_t set_endpoints(struct devicecode_ctx *dc_ctx,

static struct devicecode_ctx *get_dc_ctx(TALLOC_CTX *mem_ctx,
bool libcurl_debug, const char *ca_db,
bool use_gssapi,
const char *issuer_url,
const char *device_auth_endpoint,
const char *token_endpoint,
Expand All @@ -300,7 +301,7 @@ static struct devicecode_ctx *get_dc_ctx(TALLOC_CTX *mem_ctx,

dc_ctx->rest_ctx = get_rest_ctx(dc_ctx, libcurl_debug, ca_db,
pkcs12_client_creds, client_auth_method,
key_passwd);
key_passwd, use_gssapi);
if (dc_ctx->rest_ctx == NULL) {
DEBUG(SSSDBG_OP_FAILURE, "Failed to get curl context.\n");
ret = ENOMEM;
Expand Down Expand Up @@ -350,6 +351,8 @@ struct cli_opts {
char *scope;
char *client_secret;
bool client_secret_stdin;
bool use_gssapi;
char *keytab;
char *ca_db;
char *user_identifier_attr;
bool libcurl_debug;
Expand All @@ -375,6 +378,7 @@ static void free_cli_opts_members(struct cli_opts *opts)
sss_erase_mem_securely(opts->client_secret, strlen(opts->client_secret));
}
free(opts->client_secret);
free(opts->keytab);
free(opts->ca_db);
free(opts->user_identifier_attr);
free(opts->search_str);
Expand Down Expand Up @@ -497,6 +501,10 @@ static int parse_cli(int argc, const char *argv[], struct cli_opts *opts)
_("Client secret/PKCS#12 password (if needed)"), NULL},
{"client-secret-stdin", 0, POPT_ARG_NONE, NULL, 's',
_("Read client secret/PKCS#12 password from standard input"), NULL},
{"client-use-gssapi", 0, POPT_ARG_NONE, NULL, 'g',
_("Use GSSAPI/Kerberos for client authentication"), NULL},
{"keytab", 0, POPT_ARG_STRING, &opts->keytab, 0,
_("Keytab file for GSSAPI client authentication"), NULL},
{"idp-type", 0, POPT_ARG_STRING, &opts->idp_type, 0,
_("Type of the IdP (entra_id, keycloak etc)"), NULL},
{"name", 0, POPT_ARG_STRING, &tmp_name, 0, _("Name of user or group"),
Expand Down Expand Up @@ -534,6 +542,9 @@ static int parse_cli(int argc, const char *argv[], struct cli_opts *opts)
case 's':
opts->client_secret_stdin = true;
break;
case 'g':
opts->use_gssapi = true;
break;
case 'r':
opts->return_tokens = true;
break;
Expand Down Expand Up @@ -579,6 +590,13 @@ static int parse_cli(int argc, const char *argv[], struct cli_opts *opts)
goto done;
}

if (opts->use_gssapi && (opts->client_secret != NULL
|| opts->client_secret_stdin)) {
fprintf(stderr, "\n--client-use-gssapi and --client-secret* options "
"are mutually exclusive.\n\n");
goto done;
}

if (tmp_name != NULL) {
opts->search_str = tmp_name;
opts->search_str_type = TYPE_NAME;
Expand Down Expand Up @@ -720,6 +738,15 @@ int main(int argc, const char *argv[])
}
talloc_steal(main_ctx, debug_prg_name);

if (opts.use_gssapi) {
ret = oidc_setup_gssapi(opts.keytab);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE,
"Failed to acquire GSSAPI credentials from keytab.\n");
goto done;
}
}

if (opts.oidc_cmd == GET_DEVICE_CODE
|| IS_ID_CMD(opts.oidc_cmd)) {
if (opts.client_secret_stdin) {
Expand All @@ -746,7 +773,8 @@ int main(int argc, const char *argv[])
opts.client_id, opts.client_secret,
opts.pkcs12_client_creds,
opts.client_auth_method,
opts.token_endpoint, opts.scope, &out);
opts.token_endpoint, opts.scope,
opts.use_gssapi, &out);
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE, "Id lookup failed.\n");
goto done;
Expand All @@ -764,6 +792,7 @@ int main(int argc, const char *argv[])
|| opts.oidc_cmd == GET_ACCESS_TOKEN
|| opts.oidc_cmd == REFRESH_ACCESS_TOKEN) {
dc_ctx = get_dc_ctx(main_ctx, opts.libcurl_debug, opts.ca_db,
opts.use_gssapi,
opts.issuer_url,
opts.device_auth_endpoint, opts.token_endpoint,
opts.userinfo_endpoint, opts.jwks_uri, opts.scope,
Expand Down
Loading
Loading