Skip to content

Commit 6495543

Browse files
simo5Günther Deschner
authored andcommitted
Add impersonation support
By setting the impersonate flag to true, the acquisition of credentials will be done using constrained delegation (s4uself + s4u2proxy). To work this needs MIT Kereberos 1.11.4 or later. Previous versions have a bug in the import_cred function that prevents the library from properly importing previously exported delegated credentials. Resolves: https://fedorahosted.org/gss-proxy/ticket/95 Reviewed-by: Günther Deschner <[email protected]>
1 parent 485a2eb commit 6495543

File tree

4 files changed

+173
-55
lines changed

4 files changed

+173
-55
lines changed

proxy/man/gssproxy.conf.5.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,14 @@
161161
</listitem>
162162
</varlistentry>
163163

164+
<varlistentry>
165+
<term>impersonate (boolean)</term>
166+
<listitem>
167+
<para>Use impersonation (s4u2self + s4u2proxy) to obtain credentials</para>
168+
<para>Default: impersonate = false</para>
169+
</listitem>
170+
</varlistentry>
171+
164172
<varlistentry>
165173
<term>kernel_nfsd (boolean)</term>
166174
<listitem>

proxy/src/gp_config.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,13 @@ static int load_services(struct gp_config *cfg, struct gp_ini_context *ctx)
208208
}
209209
}
210210

211+
ret = gp_config_get_string(ctx, secname, "impersonate", &value);
212+
if (ret == 0) {
213+
if (gp_boolean_is_true(value)) {
214+
cfg->svcs[n]->impersonate = true;
215+
}
216+
}
217+
211218
ret = gp_config_get_string(ctx, secname, "socket", &value);
212219
if (ret == 0) {
213220
cfg->svcs[n]->socket = strdup(value);

proxy/src/gp_creds.c

Lines changed: 157 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ struct gp_service *gp_creds_match_conn(struct gssproxy_ctx *gpctx,
123123
return NULL;
124124
}
125125

126+
#define PWBUFLEN 2048
126127
static char *uid_to_name(uid_t uid)
127128
{
128129
struct passwd pwd, *res = NULL;
@@ -136,15 +137,13 @@ static char *uid_to_name(uid_t uid)
136137
return strdup(pwd.pw_name);
137138
}
138139

139-
#define PWBUFLEN 2048
140140
static char *get_formatted_string(const char *orig, uid_t target_uid)
141141
{
142142
int len, left, right;
143143
char *user = NULL;
144144
char *str;
145145
char *tmp;
146146
char *p;
147-
int ret;
148147

149148
str = strdup(orig);
150149
if (!str) {
@@ -215,6 +214,19 @@ static void free_cred_store_elements(gss_key_value_set_desc *cs)
215214
safefree(cs->elements);
216215
}
217216

217+
static bool try_impersonate(struct gp_service *svc,
218+
gss_cred_usage_t cred_usage)
219+
{
220+
if (!svc->impersonate) {
221+
return false;
222+
}
223+
if (cred_usage == GSS_C_ACCEPT) {
224+
return false;
225+
}
226+
227+
return true;
228+
}
229+
218230
static int gp_get_cred_environment(struct gp_call_ctx *gpcall,
219231
gssx_name *desired_name,
220232
gss_name_t *requested_name,
@@ -223,6 +235,7 @@ static int gp_get_cred_environment(struct gp_call_ctx *gpcall,
223235
{
224236
struct gp_service *svc;
225237
gss_name_t name = GSS_C_NO_NAME;
238+
gss_buffer_desc namebuf;
226239
gss_OID_desc name_type;
227240
uint32_t ret_maj = 0;
228241
uint32_t ret_min = 0;
@@ -231,10 +244,13 @@ static int gp_get_cred_environment(struct gp_call_ctx *gpcall,
231244
const char *p;
232245
char *str;
233246
bool user_requested = false;
234-
int ret = 0;
247+
bool use_service_keytab = false;
248+
int ret = -1;
235249
int k_num = -1;
236250
int ck_num = -1;
237-
int c, s;
251+
int c, d;
252+
253+
memset(cs, 0, sizeof(gss_key_value_set_desc));
238254

239255
target_uid = gp_conn_get_uid(gpcall->connection);
240256
svc = gpcall->service;
@@ -264,6 +280,8 @@ static int gp_get_cred_environment(struct gp_call_ctx *gpcall,
264280
/* it's a user request if it comes from an arbitrary uid */
265281
if (svc->euid != target_uid) {
266282
user_requested = true;
283+
} else {
284+
use_service_keytab = true;
267285
}
268286
ret_maj = gp_conv_gssx_to_name(&ret_min, desired_name, &name);
269287
if (ret_maj) {
@@ -273,90 +291,112 @@ static int gp_get_cred_environment(struct gp_call_ctx *gpcall,
273291
}
274292
}
275293

294+
/* impersonation case (only for initiation) */
295+
if (user_requested) {
296+
if (try_impersonate(svc, *cred_usage)) {
297+
/* When impersonating we want to use the service keytab to
298+
* acquire initial credential ... */
299+
use_service_keytab = true;
300+
301+
/* ... and after that make the s4u2self delegation dance with the
302+
* target name identifying the user */
303+
str = uid_to_name(target_uid);
304+
if (str == NULL) {
305+
GPERROR("Failed to get username from uid %d\n", target_uid);
306+
return ENOENT;
307+
}
308+
namebuf.value = str;
309+
namebuf.length = strlen(str);
310+
ret_maj = gss_import_name(&ret_min, &namebuf,
311+
GSS_C_NT_USER_NAME, requested_name);
312+
safefree(str);
313+
if (ret_maj) {
314+
GPERROR("Failed to import username %s\n", str);
315+
return ENOMEM;
316+
}
317+
}
318+
}
319+
276320
if (svc->krb5.cred_store == NULL) {
277-
cs->elements = NULL;
278-
cs->count = 0;
279321
return 0;
280322
}
281323

282-
cs->count = svc->krb5.cred_count;
283-
cs->elements = calloc(cs->count, sizeof(gss_key_value_element_desc));
324+
/* allocate 1 more than in source, just in case we need to add
325+
* an internal client_keytab element */
326+
cs->elements = calloc(svc->krb5.cred_count + 1,
327+
sizeof(gss_key_value_element_desc));
284328
if (!cs->elements) {
285329
ret = ENOMEM;
286330
goto done;
287331
}
288-
289-
for (c = 0; c < cs->count; c++) {
290-
p = strchr(svc->krb5.cred_store[c], ':');
332+
c = 0;
333+
for (d = 0; d < svc->krb5.cred_count; d++) {
334+
p = strchr(svc->krb5.cred_store[d], ':');
291335
if (!p) {
292336
GPERROR("Invalid cred_store value"
293337
"no ':' separator found in [%s].\n",
294-
svc->krb5.cred_store[c]);
338+
svc->krb5.cred_store[d]);
295339
ret = EINVAL;
296340
goto done;
297341
}
298-
s = p - svc->krb5.cred_store[c];
299-
str = strdup(svc->krb5.cred_store[c]);
300-
if (!str) {
342+
343+
if (strncmp(svc->krb5.cred_store[d], "client_keytab:", 14) == 0) {
344+
ck_num = c;
345+
} else if (strncmp(svc->krb5.cred_store[d], "keytab:", 7) == 0) {
346+
k_num = c;
347+
}
348+
349+
ret = asprintf(&str, "%.*s", (int)(p - svc->krb5.cred_store[d]),
350+
svc->krb5.cred_store[d]);
351+
if (ret == -1) {
301352
ret = ENOMEM;
302353
goto done;
303354
}
304-
str[s] = '\0';
305355
cs->elements[c].key = str;
306356

307-
if (strcmp(cs->elements[c].key, "keytab") == 0) {
308-
k_num = c;
309-
} else if (strcmp(cs->elements[c].key, "client_keytab") == 0) {
310-
ck_num = c;
311-
}
312-
313357
fmtstr = p + 1;
314358
cs->elements[c].value = get_formatted_string(fmtstr, target_uid);
315359
if (!cs->elements[c].value) {
316360
GPDEBUG("Failed to build credential store formatted string.\n");
317361
ret = ENOMEM;
318362
goto done;
319363
}
364+
365+
c++;
320366
}
367+
cs->count = c;
321368

322369
/* when a user is not explicitly requested then it means the calling
323370
* application wants to use the credentials in the standard keytab,
324371
* if any. */
325-
if (!user_requested && k_num != -1) {
326-
if (ck_num != -1) {
327-
safefree(cs->elements[ck_num].value);
328-
cs->elements[ck_num].value = strdup(cs->elements[k_num].value);
329-
if (!cs->elements[ck_num].value) {
330-
ret = ENOMEM;
331-
goto done;
332-
}
333-
} else {
334-
gss_key_value_element_desc *t;
335-
c = cs->count;
336-
t = realloc(cs->elements,
337-
(c + 1) * sizeof(gss_key_value_element_desc));
338-
if (!t) {
339-
ret = ENOMEM;
340-
goto done;
341-
}
342-
cs->elements = t;
343-
cs->elements[c].key = strdup("client_keytab");
344-
if (!cs->elements[c].key) {
345-
ret = ENOMEM;
346-
goto done;
347-
}
372+
if (use_service_keytab) {
373+
if (k_num == -1) {
374+
ret = EINVAL;
375+
goto done;
376+
}
377+
if (ck_num == -1) {
378+
/* we always have space for 1 more */
379+
ck_num = cs->count;
348380

349-
/* increment now so in case of failure to copy the value, key is
350-
* still freed properly */
351-
cs->count = c + 1;
352-
cs->elements[c].value = strdup(cs->elements[k_num].value);
353-
if (!cs->elements[c].value) {
381+
cs->elements[ck_num].key = strdup("client_keytab");
382+
if (!cs->elements[ck_num].key) {
354383
ret = ENOMEM;
355384
goto done;
356385
}
386+
387+
cs->count = ck_num + 1;
388+
} else {
389+
safefree(cs->elements[ck_num].value);
390+
}
391+
cs->elements[ck_num].value = strdup(cs->elements[k_num].value);
392+
if (!cs->elements[ck_num].value) {
393+
ret = ENOMEM;
394+
goto done;
357395
}
358396
}
359397

398+
ret = 0;
399+
360400
done:
361401
if (ret) {
362402
free_cred_store_elements(cs);
@@ -382,6 +422,13 @@ uint32_t gp_add_krb5_creds(uint32_t *min,
382422
gss_name_t req_name = GSS_C_NO_NAME;
383423
gss_OID_set_desc desired_mechs = { 1, &gp_mech_krb5 };
384424
gss_key_value_set_desc cred_store;
425+
gss_cred_id_t impersonator_cred = GSS_C_NO_CREDENTIAL;
426+
gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL;
427+
gss_ctx_id_t initiator_context = GSS_C_NO_CONTEXT;
428+
gss_ctx_id_t acceptor_context = GSS_C_NO_CONTEXT;
429+
gss_name_t target_name = GSS_C_NO_NAME;
430+
gss_buffer_desc init_token = GSS_C_EMPTY_BUFFER;
431+
gss_buffer_desc accept_token = GSS_C_EMPTY_BUFFER;
385432

386433
if (!min || !output_cred_handle) {
387434
return GSS_S_CALL_INACCESSIBLE_WRITE;
@@ -408,11 +455,60 @@ uint32_t gp_add_krb5_creds(uint32_t *min,
408455
goto done;
409456
}
410457

411-
ret_maj = gss_acquire_cred_from(&ret_min, req_name, GSS_C_INDEFINITE,
412-
&desired_mechs, cred_usage, &cred_store,
413-
output_cred_handle, actual_mechs, NULL);
414-
if (ret_maj) {
415-
goto done;
458+
if (!try_impersonate(gpcall->service, cred_usage)) {
459+
ret_maj = gss_acquire_cred_from(&ret_min, req_name, GSS_C_INDEFINITE,
460+
&desired_mechs, cred_usage,
461+
&cred_store, output_cred_handle,
462+
actual_mechs, NULL);
463+
if (ret_maj) {
464+
goto done;
465+
}
466+
} else { /* impersonation */
467+
ret_maj = gss_acquire_cred_from(&ret_min, GSS_C_NO_NAME,
468+
GSS_C_INDEFINITE,
469+
&desired_mechs, GSS_C_BOTH,
470+
&cred_store, &impersonator_cred,
471+
NULL, NULL);
472+
if (ret_maj) {
473+
goto done;
474+
}
475+
ret_maj = gss_inquire_cred(&ret_min, impersonator_cred,
476+
&target_name, NULL, NULL, NULL);
477+
if (ret_maj) {
478+
goto done;
479+
}
480+
481+
ret_maj = gss_acquire_cred_impersonate_name(&ret_min,
482+
impersonator_cred,
483+
req_name,
484+
GSS_C_INDEFINITE,
485+
&desired_mechs,
486+
GSS_C_INITIATE,
487+
&user_cred,
488+
actual_mechs, NULL);
489+
if (ret_maj) {
490+
goto done;
491+
}
492+
/* now acquire credentials for impersonated user to self */
493+
ret_maj = gss_init_sec_context(&ret_min, user_cred, &initiator_context,
494+
target_name, &gp_mech_krb5,
495+
GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
496+
GSS_C_INDEFINITE,
497+
GSS_C_NO_CHANNEL_BINDINGS,
498+
GSS_C_NO_BUFFER, NULL,
499+
&init_token, NULL, NULL);
500+
if (ret_maj) {
501+
goto done;
502+
}
503+
/* accept context to be able to store delgated credentials */
504+
ret_maj = gss_accept_sec_context(&ret_min, &acceptor_context,
505+
impersonator_cred, &init_token,
506+
GSS_C_NO_CHANNEL_BINDINGS,
507+
NULL, NULL, &accept_token,
508+
NULL, NULL, output_cred_handle);
509+
if (ret_maj) {
510+
goto done;
511+
}
416512
}
417513

418514
if (initiator_time_rec || acceptor_time_rec) {
@@ -437,6 +533,12 @@ uint32_t gp_add_krb5_creds(uint32_t *min,
437533
gss_release_oid_set(&discard, actual_mechs);
438534
}
439535
}
536+
gss_release_cred(&discard, &impersonator_cred);
537+
gss_release_cred(&discard, &user_cred);
538+
gss_release_name(&discard, &target_name);
539+
gss_delete_sec_context(&discard, &initiator_context, NULL);
540+
gss_release_buffer(&discard, &init_token);
541+
gss_release_buffer(&discard, &accept_token);
440542
*min = ret_min;
441543

442544
return ret_maj;

proxy/src/gp_proxy.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ struct gp_service {
5353
bool any_uid;
5454
bool trusted;
5555
bool kernel_nfsd;
56+
bool impersonate;
5657
char *socket;
5758
SELINUX_CTX selinux_ctx;
5859
gss_cred_usage_t cred_usage;

0 commit comments

Comments
 (0)