Path: blob/main/crypto/krb5/src/clients/ksu/heuristic.c
34889 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* Copyright (c) 1994 by the University of Southern California3*4* EXPORT OF THIS SOFTWARE from the United States of America may5* require a specific license from the United States Government.6* It is the responsibility of any person or organization contemplating7* export to obtain such a license before exporting.8*9* WITHIN THAT CONSTRAINT, permission to copy, modify, and distribute10* this software and its documentation in source and binary forms is11* hereby granted, provided that any documentation or other materials12* related to such distribution or use acknowledge that the software13* was developed by the University of Southern California.14*15* DISCLAIMER OF WARRANTY. THIS SOFTWARE IS PROVIDED "AS IS". The16* University of Southern California MAKES NO REPRESENTATIONS OR17* WARRANTIES, EXPRESS OR IMPLIED. By way of example, but not18* limitation, the University of Southern California MAKES NO19* REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY20* PARTICULAR PURPOSE. The University of Southern21* California shall not be held liable for any liability nor for any22* direct, indirect, or consequential damages with respect to any23* claim by the user or distributor of the ksu software.24*25* KSU was written by: Ari Medvinsky, [email protected]26*/2728#include "ksu.h"2930#ifdef HAVE_UNISTD_H31#include <unistd.h>32#endif333435/*******************************************************************36get_all_princ_from_file - retrieves all principal names37from file pointed to by fp.3839*******************************************************************/40static void close_time (int, FILE *, int, FILE *);41static krb5_boolean find_str_in_list (char **, char *);4243krb5_error_code44get_all_princ_from_file(FILE *fp, char ***plist)45{4647krb5_error_code retval;48char * line, * fprinc, * lp, ** temp_list = NULL;49int count = 0, chunk_count = 1;5051if (!(temp_list = (char **) malloc( CHUNK * sizeof(char *))))52return ENOMEM;5354retval = get_line(fp, &line);55if (retval)56return retval;5758while (line){59fprinc = get_first_token (line, &lp);6061if (fprinc ){62temp_list[count] = xstrdup(fprinc);63count ++;64}6566if(count == (chunk_count * CHUNK -1)){67chunk_count ++;68if (!(temp_list = (char **) realloc(temp_list,69chunk_count * CHUNK * sizeof(char *)))){70return ENOMEM;71}72}737475free (line);76retval = get_line(fp, &line);77if (retval)78return retval;79}8081temp_list[count] = NULL;8283*plist = temp_list;84return 0;85}8687/*************************************************************88list_union - combines list1 and list2 into combined_list.89the space for list1 and list2 is either freed90or used by combined_list.91**************************************************************/9293krb5_error_code94list_union(char **list1, char **list2, char ***combined_list)95{9697unsigned int c1 =0, c2 = 0, i=0, j=0;98char ** tlist;99100if (! list1){101*combined_list = list2;102return 0;103}104105if (! list2){106*combined_list = list1;107return 0;108}109110while (list1[c1]) c1++;111while (list2[c2]) c2++;112113if (!(tlist = (char **) calloc( c1 + c2 + 1, sizeof ( char *))))114return ENOMEM;115116i = 0;117while(list1[i]) {118tlist[i] = list1[i];119i++;120}121j = 0;122while(list2[j]){123if(find_str_in_list(list1, list2[j])==FALSE){124tlist[i] = list2[j];125i++;126}127j++;128}129130free (list1);131free (list2);132133tlist[i]= NULL;134135*combined_list = tlist;136return 0;137}138139krb5_error_code140filter(FILE *fp, char *cmd, char **k5users_list, char ***k5users_filt_list)141{142143krb5_error_code retval =0;144krb5_boolean found = FALSE;145char * out_cmd = NULL;146unsigned int i=0, j=0, found_count = 0, k=0;147char ** temp_filt_list;148149*k5users_filt_list = NULL;150151if (k5users_list == NULL)152return 0;153154while(k5users_list[i]){155free(out_cmd);156out_cmd = NULL;157158retval= k5users_lookup(fp, k5users_list[i], cmd, &found, &out_cmd);159if (retval)160goto cleanup;161162if (found == FALSE){163free (k5users_list[i]);164k5users_list[i] = NULL;165if (out_cmd) {166gb_err = out_cmd;167out_cmd = NULL;168}169} else170found_count ++;171172i++;173}174175temp_filt_list = xcalloc(found_count + 1, sizeof(*temp_filt_list));176177for(j= 0, k=0; j < i; j++ ) {178if (k5users_list[j]){179temp_filt_list[k] = k5users_list[j];180k++;181}182}183184temp_filt_list[k] = NULL;185186free (k5users_list);187188*k5users_filt_list = temp_filt_list;189190cleanup:191free(out_cmd);192return retval;193}194195krb5_error_code196get_authorized_princ_names(const char *luser, char *cmd, char ***princ_list)197{198199struct passwd *pwd;200int k5login_flag =0;201int k5users_flag =0;202FILE * login_fp = NULL , * users_fp = NULL;203char ** k5login_list = NULL, ** k5users_list = NULL;204char ** k5users_filt_list = NULL;205char ** combined_list = NULL;206struct stat tb;207krb5_error_code retval;208209*princ_list = NULL;210211/* no account => no access */212213if ((pwd = getpwnam(luser)) == NULL)214return 0;215216k5login_flag = stat(k5login_path, &tb);217k5users_flag = stat(k5users_path, &tb);218219if (!k5login_flag){220if ((login_fp = fopen(k5login_path, "r")) == NULL)221return 0;222if ( fowner(login_fp, pwd->pw_uid) == FALSE){223close_time(1 /*k5users_flag*/, (FILE *) 0 /*users_fp*/,224k5login_flag,login_fp);225return 0;226}227}228if (!k5users_flag){229users_fp = fopen(k5users_path, "r");230if (users_fp == NULL) {231close_time(1, NULL, k5login_flag, login_fp);232return 0;233}234235if ( fowner(users_fp, pwd->pw_uid) == FALSE){236close_time(k5users_flag,users_fp, k5login_flag,login_fp);237return 0;238}239240retval = get_all_princ_from_file (users_fp, &k5users_list);241if(retval) {242close_time(k5users_flag,users_fp, k5login_flag,login_fp);243return retval;244}245246rewind(users_fp);247248retval = filter(users_fp,cmd, k5users_list, &k5users_filt_list);249if(retval) {250close_time(k5users_flag,users_fp, k5login_flag, login_fp);251return retval;252}253}254255if (!k5login_flag){256retval = get_all_princ_from_file (login_fp, &k5login_list);257if(retval) {258close_time(k5users_flag,users_fp, k5login_flag,login_fp);259return retval;260}261}262263close_time(k5users_flag,users_fp, k5login_flag, login_fp);264265retval = list_union(k5login_list, k5users_filt_list, &combined_list);266if (retval){267return retval;268}269*princ_list = combined_list;270return 0;271}272273static void274close_time(int k5users_flag, FILE *users_fp, int k5login_flag, FILE *login_fp)275{276277if (!k5users_flag) fclose(users_fp);278if (!k5login_flag) fclose(login_fp);279280}281282static krb5_boolean283find_str_in_list(char **list, char *elm)284{285286int i=0;287krb5_boolean found = FALSE;288289if (!list) return found;290291while (list[i] ){292if (!strcmp(list[i], elm)){293found = TRUE;294break;295}296i++;297}298299return found;300}301302/**********************************************************************303returns the principal that is closes to client (can be the the client304himself). plist contains305a principal list obtained from .k5login and .k5users file.306A principal is picked that has the best chance of getting in.307308**********************************************************************/309310krb5_error_code311get_closest_principal(krb5_context context, char **plist,312krb5_principal *client, krb5_boolean *found)313{314krb5_error_code retval =0;315krb5_principal temp_client, best_client = NULL;316int i = 0, j=0, cnelem, pnelem;317krb5_boolean got_one;318319*found = FALSE;320321if (! plist ) return 0;322323cnelem = krb5_princ_size(context, *client);324325while(plist[i]){326327retval = krb5_parse_name(context, plist[i], &temp_client);328if (retval)329goto cleanup;330331pnelem = krb5_princ_size(context, temp_client);332333if ( cnelem > pnelem){334i++;335continue;336}337338if (data_eq(*krb5_princ_realm(context, *client),339*krb5_princ_realm(context, temp_client))) {340341got_one = TRUE;342for(j =0; j < cnelem; j ++){343krb5_data *p1 =344krb5_princ_component(context, *client, j);345krb5_data *p2 =346krb5_princ_component(context, temp_client, j);347348if (!p1 || !p2 || !data_eq(*p1, *p2)) {349got_one = FALSE;350break;351}352}353if (got_one == TRUE){354if(best_client){355if(krb5_princ_size(context, best_client) >356krb5_princ_size(context, temp_client)){357krb5_free_principal(context, best_client);358best_client = temp_client;359}360}else361best_client = temp_client;362}363}364i++;365}366367if (best_client) {368*found = TRUE;369*client = best_client;370best_client = NULL;371}372373cleanup:374krb5_free_principal(context, best_client);375return retval;376}377378/****************************************************************379find_either_ticket checks to see whether there is a ticket for the380end server or tgt, if neither is there the return FALSE,381*****************************************************************/382383krb5_error_code384find_either_ticket(krb5_context context, krb5_ccache cc, krb5_principal client,385krb5_principal end_server, krb5_boolean *found)386{387388krb5_principal kdc_server;389krb5_error_code retval;390krb5_boolean temp_found = FALSE;391392if (ks_ccache_is_initialized(context, cc)) {393394retval = find_ticket(context, cc, client, end_server, &temp_found);395if (retval)396return retval;397398if (temp_found == FALSE){399retval = ksu_tgtname(context,400krb5_princ_realm(context, client),401krb5_princ_realm(context, client),402&kdc_server);403if (retval)404return retval;405406retval = find_ticket(context, cc,client, kdc_server, &temp_found);407if(retval)408return retval;409}410else if (auth_debug)411printf("find_either_ticket: found end server ticket\n");412}413414*found = temp_found;415416return 0;417}418419krb5_error_code420find_ticket(krb5_context context, krb5_ccache cc, krb5_principal client,421krb5_principal server, krb5_boolean *found)422{423424krb5_creds tgt, tgtq;425krb5_error_code retval;426427*found = FALSE;428429memset(&tgtq, 0, sizeof(tgtq));430memset(&tgt, 0, sizeof(tgt));431432retval= krb5_copy_principal(context, client, &tgtq.client);433if (retval)434return retval;435436retval= krb5_copy_principal(context, server, &tgtq.server);437if (retval)438return retval ;439440retval = krb5_cc_retrieve_cred(context, cc, KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES,441&tgtq, &tgt);442443if (! retval) retval = krb5_check_exp(context, tgt.times);444445if (retval){446if ((retval != KRB5_CC_NOTFOUND) &&447(retval != KRB5KRB_AP_ERR_TKT_EXPIRED)){448return retval ;449}450} else{451*found = TRUE;452return 0;453}454455free(tgtq.server);456free(tgtq.client);457458return 0;459}460461krb5_error_code462find_princ_in_list(krb5_context context, krb5_principal princ, char **plist,463krb5_boolean *found)464{465466int i=0;467char * princname;468krb5_error_code retval;469470*found = FALSE;471472if (!plist) return 0;473474retval = krb5_unparse_name(context, princ, &princname);475if (retval)476return retval;477478while (plist[i] ){479if (!strcmp(plist[i], princname)){480*found = TRUE;481break;482}483i++;484}485486free(princname);487return 0;488489}490491typedef struct princ_info {492krb5_principal p;493krb5_boolean found;494}princ_info;495496/**********************************************************************497get_best_princ_for_target -498499sets the client name, path_out gets set, if authorization is not possible500path_out gets set to ...501502***********************************************************************/503504krb5_error_code505get_best_princ_for_target(krb5_context context, uid_t source_uid,506uid_t target_uid, char *source_user,507char *target_user, krb5_ccache cc_source,508krb5_get_init_creds_opt *options, char *cmd,509char *hostname, krb5_principal *client,510int *path_out)511{512513princ_info princ_trials[10];514krb5_principal cc_def_princ = NULL, temp_client = NULL;515krb5_principal target_client = NULL, source_client = NULL;516krb5_principal end_server = NULL;517krb5_error_code retval;518char ** aplist =NULL;519krb5_boolean found = FALSE;520struct stat tb;521int count =0;522int i;523524*path_out = 0;525526/* -n option was specified client is set we are done */527if (*client != NULL)528return 0;529530if (ks_ccache_is_initialized(context, cc_source)) {531retval = krb5_cc_get_principal(context, cc_source, &cc_def_princ);532if (retval)533goto cleanup;534}535536retval=krb5_parse_name(context, target_user, &target_client);537if (retval)538goto cleanup;539540retval=krb5_parse_name(context, source_user, &source_client);541if (retval)542goto cleanup;543544if (source_uid == 0) {545if (target_uid != 0) {546/* This will be used to restrict the cache copy. */547*client = target_client;548target_client = NULL;549} else if (cc_def_princ != NULL) {550*client = cc_def_princ;551cc_def_princ = NULL;552} else {553*client = target_client;554target_client = NULL;555}556if (auth_debug)557printf(" GET_best_princ_for_target: via source_uid == 0\n");558goto cleanup;559}560561/* from here on, the code is for source_uid != 0 */562563if (source_uid && (source_uid == target_uid)){564if (cc_def_princ != NULL) {565*client = cc_def_princ;566cc_def_princ = NULL;567} else {568*client = target_client;569target_client = NULL;570}571if (auth_debug)572printf("GET_best_princ_for_target: via source_uid == target_uid\n");573goto cleanup;574}575576/* Become root, then target for looking at .k5login.*/577if (krb5_seteuid(0) || krb5_seteuid(target_uid) ) {578retval = errno;579goto cleanup;580}581582/* if .k5users and .k5login do not exist */583if (stat(k5login_path, &tb) && stat(k5users_path, &tb) ){584*client = target_client;585target_client = NULL;586587if (cmd)588*path_out = NOT_AUTHORIZED;589590if (auth_debug)591printf(" GET_best_princ_for_target: via no auth files path\n");592593goto cleanup;594}else{595retval = get_authorized_princ_names(target_user, cmd, &aplist);596if (retval)597goto cleanup;598599/* .k5users or .k5login exist, but no authorization */600if ((!aplist) || (!aplist[0])) {601*path_out = NOT_AUTHORIZED;602if (auth_debug)603printf("GET_best_princ_for_target: via empty auth files path\n");604goto cleanup;605}606}607608retval = krb5_sname_to_principal(context, hostname, NULL,609KRB5_NT_SRV_HST, &end_server);610if (retval)611goto cleanup;612613/* first see if default principal of the source cache614* can get us in, then the target_user@realm, then the615* source_user@realm. If all of them fail, try any616* other ticket in the cache. */617618if (cc_def_princ)619princ_trials[count ++].p = cc_def_princ;620else621princ_trials[count ++].p = NULL;622623princ_trials[count ++].p = target_client;624princ_trials[count ++].p = source_client;625626for (i= 0; i < count; i ++)627princ_trials[i].found = FALSE;628629for (i= 0; i < count; i ++){630if(princ_trials[i].p) {631retval= find_princ_in_list(context, princ_trials[i].p, aplist,632&found);633if (retval)634goto cleanup;635636if (found == TRUE){637princ_trials[i].found = TRUE;638639retval = find_either_ticket (context, cc_source,640princ_trials[i].p,641end_server, &found);642if (retval)643goto cleanup;644if (found == TRUE){645retval = krb5_copy_principal(context, princ_trials[i].p,646client);647if (auth_debug)648printf("GET_best_princ_for_target: via ticket file, choice #%d\n", i);649goto cleanup;650}651}652}653}654655/* out of preferred principals, see if there is any ticket that will656get us in */657658i=0;659while (aplist[i]){660retval = krb5_parse_name(context, aplist[i], &temp_client);661if (retval)662goto cleanup;663664retval = find_either_ticket (context, cc_source, temp_client,665end_server, &found);666if (retval)667goto cleanup;668669if (found == TRUE){670if (auth_debug)671printf("GET_best_princ_for_target: via ticket file, choice: any ok ticket \n" );672*client = temp_client;673temp_client = NULL;674goto cleanup;675}676677krb5_free_principal(context, temp_client);678temp_client = NULL;679680i++;681}682683/* no tickets qualified, select a principal, that may be used684for password promting */685686687for (i=0; i < count; i ++){688if (princ_trials[i].found == TRUE){689retval = krb5_copy_principal(context, princ_trials[i].p, client);690691if (auth_debug)692printf("GET_best_princ_for_target: via prompt passwd list choice #%d \n",i);693goto cleanup;694}695}696697#ifdef PRINC_LOOK_AHEAD698for (i=0; i < count; i ++){699if (princ_trials[i].p){700retval=krb5_copy_principal(context, princ_trials[i].p,701&temp_client);702if(retval)703goto cleanup;704705/* get the client name that is the closest706to the three princ in trials */707708retval=get_closest_principal(context, aplist, &temp_client,709&found);710if(retval)711goto cleanup;712713if (found == TRUE){714*client = temp_client;715temp_client = NULL;716if (auth_debug)717printf("GET_best_princ_for_target: via prompt passwd list choice: approximation of princ in trials # %d \n",i);718goto cleanup;719}720}721}722723#endif /* PRINC_LOOK_AHEAD */724725726if(auth_debug)727printf( "GET_best_princ_for_target: out of luck, can't get appropriate default principal\n");728729*path_out = NOT_AUTHORIZED;730retval = 0;731732cleanup:733krb5_free_principal(context, cc_def_princ);734krb5_free_principal(context, target_client);735krb5_free_principal(context, source_client);736krb5_free_principal(context, temp_client);737krb5_free_principal(context, end_server);738return retval;739}740741742