@@ -123,6 +123,7 @@ struct gp_service *gp_creds_match_conn(struct gssproxy_ctx *gpctx,
123123 return NULL ;
124124}
125125
126+ #define PWBUFLEN 2048
126127static 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
140140static 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+
218230static 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+
360400done :
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 ;
0 commit comments