#include "ksu.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
static void close_time (int, FILE *, int, FILE *);
static krb5_boolean find_str_in_list (char **, char *);
krb5_error_code get_all_princ_from_file (fp, plist)
FILE *fp;
char ***plist;
{
krb5_error_code retval;
char * line, * fprinc, * lp, ** temp_list = NULL;
int count = 0, chunk_count = 1;
if (!(temp_list = (char **) malloc( CHUNK * sizeof(char *))))
return errno;
retval = get_line(fp, &line);
if (retval)
return retval;
while (line){
fprinc = get_first_token (line, &lp);
if (fprinc ){
temp_list[count] = xstrdup(fprinc);
count ++;
}
if(count == (chunk_count * CHUNK -1)){
chunk_count ++;
if (!(temp_list = (char **) realloc(temp_list,
chunk_count * CHUNK * sizeof(char *)))){
return errno;
}
}
free (line);
retval = get_line(fp, &line);
if (retval)
return retval;
}
temp_list[count] = NULL;
*plist = temp_list;
return 0;
}
krb5_error_code list_union(list1, list2, combined_list)
char **list1;
char **list2;
char ***combined_list;
{
unsigned int c1 =0, c2 = 0, i=0, j=0;
char ** tlist;
if (! list1){
*combined_list = list2;
return 0;
}
if (! list2){
*combined_list = list1;
return 0;
}
while (list1[c1]) c1++;
while (list2[c2]) c2++;
if (!(tlist = (char **) calloc( c1 + c2 + 1, sizeof ( char *))))
return errno;
i = 0;
while(list1[i]) {
tlist[i] = list1[i];
i++;
}
j = 0;
while(list2[j]){
if(find_str_in_list(list1, list2[j])==FALSE){
tlist[i] = list2[j];
i++;
}
j++;
}
free (list1);
free (list2);
tlist[i]= NULL;
*combined_list = tlist;
return 0;
}
krb5_error_code
filter(fp, cmd, k5users_list, k5users_filt_list)
FILE *fp;
char *cmd;
char **k5users_list;
char ***k5users_filt_list;
{
krb5_error_code retval =0;
krb5_boolean found = FALSE;
char * out_cmd = NULL;
unsigned int i=0, j=0, found_count = 0, k=0;
char ** temp_filt_list;
*k5users_filt_list = NULL;
if (! k5users_list){
return 0;
}
while(k5users_list[i]){
retval= k5users_lookup(fp, k5users_list[i], cmd, &found, &out_cmd);
if (retval)
return retval;
if (found == FALSE){
free (k5users_list[i]);
k5users_list[i] = NULL;
if (out_cmd) gb_err = out_cmd;
} else
found_count ++;
i++;
}
if (! (temp_filt_list = (char **) calloc(found_count +1, sizeof (char*))))
return errno;
for(j= 0, k=0; j < i; j++ ) {
if (k5users_list[j]){
temp_filt_list[k] = k5users_list[j];
k++;
}
}
temp_filt_list[k] = NULL;
free (k5users_list);
*k5users_filt_list = temp_filt_list;
return 0;
}
krb5_error_code
get_authorized_princ_names(luser, cmd, princ_list)
const char *luser;
char *cmd;
char ***princ_list;
{
struct passwd *pwd;
int k5login_flag =0;
int k5users_flag =0;
FILE * login_fp = NULL , * users_fp = NULL;
char ** k5login_list = NULL, ** k5users_list = NULL;
char ** k5users_filt_list = NULL;
char ** combined_list = NULL;
struct stat tb;
krb5_error_code retval;
*princ_list = NULL;
if ((pwd = getpwnam(luser)) == NULL)
return 0;
k5login_flag = stat(k5login_path, &tb);
k5users_flag = stat(k5users_path, &tb);
if (!k5login_flag){
if ((login_fp = fopen(k5login_path, "r")) == NULL)
return 0;
if ( fowner(login_fp, pwd->pw_uid) == FALSE){
close_time(1 , (FILE *) 0 ,
k5login_flag,login_fp);
return 0;
}
}
if (!k5users_flag){
if ((users_fp = fopen(k5users_path, "r")) == NULL)
return 0;
if ( fowner(users_fp, pwd->pw_uid) == FALSE){
close_time(k5users_flag,users_fp, k5login_flag,login_fp);
return 0;
}
retval = get_all_princ_from_file (users_fp, &k5users_list);
if(retval) {
close_time(k5users_flag,users_fp, k5login_flag,login_fp);
return retval;
}
rewind(users_fp);
retval = filter(users_fp,cmd, k5users_list, &k5users_filt_list);
if(retval) {
close_time(k5users_flag,users_fp, k5login_flag, login_fp);
return retval;
}
}
if (!k5login_flag){
retval = get_all_princ_from_file (login_fp, &k5login_list);
if(retval) {
close_time(k5users_flag,users_fp, k5login_flag,login_fp);
return retval;
}
}
close_time(k5users_flag,users_fp, k5login_flag, login_fp);
if (cmd) {
retval = list_union(k5login_list, k5users_filt_list, &combined_list);
if (retval){
close_time(k5users_flag,users_fp, k5login_flag,login_fp);
return retval;
}
*princ_list = combined_list;
return 0;
} else {
if (k5users_filt_list != NULL)
free(k5users_filt_list);
*princ_list = k5login_list;
return 0;
}
}
static void close_time(k5users_flag, users_fp, k5login_flag, login_fp)
int k5users_flag;
FILE *users_fp;
int k5login_flag;
FILE *login_fp;
{
if (!k5users_flag) fclose(users_fp);
if (!k5login_flag) fclose(login_fp);
}
static krb5_boolean find_str_in_list(list , elm)
char **list;
char *elm;
{
int i=0;
krb5_boolean found = FALSE;
if (!list) return found;
while (list[i] ){
if (!strcmp(list[i], elm)){
found = TRUE;
break;
}
i++;
}
return found;
}
krb5_error_code get_closest_principal(context, plist, client, found)
krb5_context context;
char **plist;
krb5_principal *client;
krb5_boolean *found;
{
krb5_error_code retval =0;
krb5_principal temp_client, best_client = NULL;
int i = 0, j=0, cnelem, pnelem;
krb5_boolean got_one;
*found = FALSE;
if (! plist ) return 0;
cnelem = krb5_princ_size(context, *client);
while(plist[i]){
retval = krb5_parse_name(context, plist[i], &temp_client);
if (retval)
return retval;
pnelem = krb5_princ_size(context, temp_client);
if ( cnelem > pnelem){
i++;
continue;
}
if (krb5_princ_realm(context, *client)->length ==
krb5_princ_realm(context, temp_client)->length
&& (!memcmp (krb5_princ_realm(context, *client)->data,
krb5_princ_realm(context, temp_client)->data,
krb5_princ_realm(context, temp_client)->length))){
got_one = TRUE;
for(j =0; j < cnelem; j ++){
krb5_data *p1 =
krb5_princ_component(context, *client, j);
krb5_data *p2 =
krb5_princ_component(context, temp_client, j);
if (!p1 || !p2 || (p1->length != p2->length) ||
memcmp(p1->data,p2->data,p1->length)){
got_one = FALSE;
break;
}
}
if (got_one == TRUE){
if(best_client){
if(krb5_princ_size(context, best_client) >
krb5_princ_size(context, temp_client)){
best_client = temp_client;
}
}else
best_client = temp_client;
}
}
i++;
}
if (best_client) {
*found = TRUE;
*client = best_client;
}
return 0;
}
krb5_error_code find_either_ticket (context, cc, client, end_server, found)
krb5_context context;
krb5_ccache cc;
krb5_principal client;
krb5_principal end_server;
krb5_boolean *found;
{
krb5_principal kdc_server;
krb5_error_code retval;
krb5_boolean temp_found = FALSE;
const char * cc_source_name;
struct stat st_temp;
cc_source_name = krb5_cc_get_name(context, cc);
if ( ! stat(cc_source_name, &st_temp)){
retval = find_ticket(context, cc, client, end_server, &temp_found);
if (retval)
return retval;
if (temp_found == FALSE){
retval = ksu_tgtname(context,
krb5_princ_realm(context, client),
krb5_princ_realm(context, client),
&kdc_server);
if (retval)
return retval;
retval = find_ticket(context, cc,client, kdc_server, &temp_found);
if(retval)
return retval;
}
else if (auth_debug)
printf("find_either_ticket: found end server ticket\n");
}
*found = temp_found;
return 0;
}
krb5_error_code find_ticket (context, cc, client, server, found)
krb5_context context;
krb5_ccache cc;
krb5_principal client;
krb5_principal server;
krb5_boolean *found;
{
krb5_creds tgt, tgtq;
krb5_error_code retval;
*found = FALSE;
memset((char *) &tgtq, 0, sizeof(tgtq));
memset((char *) &tgt, 0, sizeof(tgt));
retval= krb5_copy_principal(context, client, &tgtq.client);
if (retval)
return retval;
retval= krb5_copy_principal(context, server, &tgtq.server);
if (retval)
return retval ;
retval = krb5_cc_retrieve_cred(context, cc, KRB5_TC_MATCH_SRV_NAMEONLY | KRB5_TC_SUPPORTED_KTYPES,
&tgtq, &tgt);
if (! retval) retval = krb5_check_exp(context, tgt.times);
if (retval){
if ((retval != KRB5_CC_NOTFOUND) &&
(retval != KRB5KRB_AP_ERR_TKT_EXPIRED)){
return retval ;
}
} else{
*found = TRUE;
return 0;
}
free(tgtq.server);
free(tgtq.client);
return 0;
}
krb5_error_code find_princ_in_list (context, princ, plist, found)
krb5_context context;
krb5_principal princ;
char **plist;
krb5_boolean *found;
{
int i=0;
char * princname;
krb5_error_code retval;
*found = FALSE;
if (!plist) return 0;
retval = krb5_unparse_name(context, princ, &princname);
if (retval)
return retval;
while (plist[i] ){
if (!strcmp(plist[i], princname)){
*found = TRUE;
break;
}
i++;
}
return 0;
}
typedef struct princ_info {
krb5_principal p;
krb5_boolean found;
}princ_info;
krb5_error_code get_best_princ_for_target(context, source_uid, target_uid,
source_user, target_user,
cc_source, options, cmd,
hostname, client, path_out)
krb5_context context;
uid_t source_uid;
uid_t target_uid;
char *source_user;
char *target_user;
krb5_ccache cc_source;
opt_info *options;
char *cmd;
char *hostname;
krb5_principal *client;
int *path_out;
{
princ_info princ_trials[10];
const char * cc_source_name;
krb5_principal cc_def_princ = NULL;
krb5_principal temp_client;
krb5_principal target_client;
krb5_principal source_client;
krb5_principal end_server;
krb5_error_code retval;
char ** aplist =NULL;
krb5_boolean found = FALSE;
struct stat tb;
int count =0;
int i;
struct stat st_temp;
*path_out = 0;
if (options->princ)
return 0;
cc_source_name = krb5_cc_get_name(context, cc_source);
if (! stat(cc_source_name, &st_temp)) {
retval = krb5_cc_get_principal(context, cc_source, &cc_def_princ);
if (retval)
return retval;
}
retval=krb5_parse_name(context, target_user, &target_client);
if (retval)
return retval;
retval=krb5_parse_name(context, source_user, &source_client);
if (retval)
return retval;
if (source_uid == 0){
if (target_uid != 0)
*client = target_client;
else {
if(cc_def_princ)
*client = cc_def_princ;
else
*client = target_client;
}
if (auth_debug)
printf(" GET_best_princ_for_target: via source_uid == 0\n");
return 0;
}
if (source_uid && (source_uid == target_uid)){
if(cc_def_princ)
*client = cc_def_princ;
else
*client = target_client;
if (auth_debug)
printf("GET_best_princ_for_target: via source_uid == target_uid\n");
return 0;
}
if (krb5_seteuid(0) || krb5_seteuid(target_uid) ) {
return errno;
}
if (stat(k5login_path, &tb) && stat(k5users_path, &tb) ){
*client = target_client;
if (cmd)
*path_out = NOT_AUTHORIZED;
if (auth_debug)
printf(" GET_best_princ_for_target: via no auth files path\n");
return 0;
}else{
retval = get_authorized_princ_names(target_user, cmd, &aplist);
if (retval)
return retval;
if ((!aplist) || (!aplist[0])) {
*path_out = NOT_AUTHORIZED;
if (auth_debug)
printf("GET_best_princ_for_target: via empty auth files path\n");
return 0;
}
}
retval = krb5_sname_to_principal(context, hostname, NULL,
KRB5_NT_SRV_HST, &end_server);
if (retval)
return retval;
if (cc_def_princ)
princ_trials[count ++].p = cc_def_princ;
else
princ_trials[count ++].p = NULL;
princ_trials[count ++].p = target_client;
princ_trials[count ++].p = source_client;
for (i= 0; i < count; i ++)
princ_trials[i].found = FALSE;
for (i= 0; i < count; i ++){
if(princ_trials[i].p) {
retval= find_princ_in_list(context, princ_trials[i].p, aplist,
&found);
if (retval)
return retval;
if (found == TRUE){
princ_trials[i].found = TRUE;
retval = find_either_ticket (context, cc_source,
princ_trials[i].p,
end_server, &found);
if (retval)
return retval;
if (found == TRUE){
*client = princ_trials[i].p;
if (auth_debug)
printf("GET_best_princ_for_target: via ticket file, choice #%d\n", i);
return 0;
}
}
}
}
i=0;
while (aplist[i]){
retval = krb5_parse_name(context, aplist[i], &temp_client);
if (retval)
return retval;
retval = find_either_ticket (context, cc_source, temp_client,
end_server, &found);
if (retval)
return retval;
if (found == TRUE){
if (auth_debug)
printf("GET_best_princ_for_target: via ticket file, choice: any ok ticket \n" );
*client = temp_client;
return 0;
}
krb5_free_principal(context, temp_client);
i++;
}
for (i=0; i < count; i ++){
if (princ_trials[i].found == TRUE){
*client = princ_trials[i].p;
if (auth_debug)
printf("GET_best_princ_for_target: via prompt passwd list choice #%d \n",i);
return 0;
}
}
#ifdef PRINC_LOOK_AHEAD
for (i=0; i < count; i ++){
if (princ_trials[i].p){
retval=krb5_copy_principal(context, princ_trials[i].p,
&temp_client);
if(retval)
return retval;
retval=get_closest_principal(context, aplist, &temp_client,
&found);
if(retval)
return retval;
if (found == TRUE){
*client = temp_client;
if (auth_debug)
printf("GET_best_princ_for_target: via prompt passwd list choice: approximation of princ in trials # %d \n",i);
return 0;
}
krb5_free_principal(context, temp_client);
}
}
#endif
if(auth_debug)
printf( "GET_best_princ_for_target: out of luck, can't get appropriate default principal\n");
*path_out = NOT_AUTHORIZED;
return 0;
}