/* MODULE: krbtf */
/* 
 * Copyright (c) 2001-2016 Carnegie Mellon University.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer. 
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The name "Carnegie Mellon University" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For permission or any other legal
 *    details, please contact  
 *      Carnegie Mellon University
 *      Center for Technology Transfer and Enterprise Creation
 *      4615 Forbes Avenue
 *      Suite 302
 *      Pittsburgh, PA  15213
 *      (412) 268-7393, fax: (412) 268-7395
 *      innovation@andrew.cmu.edu
 *
 * 4. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by Computing Services
 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
 *
 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

/*
 * Dec  4, 2002 by Dave Eckhardt <davide+receptionist@cs.cmu.edu>
 * This is inspired by code which was identical in both
 * auth_krb4.c and auth_krb5.c.  This code is shared
 * between the two implementations, contains protection
 * against a race condition, and, when possible, uses
 * Heimdal krb5's memory-only credential caches to avoid
 * needless disk i/o.
 */

/* PUBLIC DEPENDENCIES */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>

#ifdef SASLAUTHD_THREADED /* is this really used??? */
#include <pthread.h>
#endif /* SASLAUTHD_THREADED */

#include "mechanisms.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <syslog.h>

#ifdef AUTH_KRB4
#include <auth_krb4.h>
#define WANT_KRBTF
#endif /* WANT_KRBTF */

#ifdef AUTH_KRB5
#include <auth_krb5.h>
#define WANT_KRBTF
#endif /* WANT_KRBTF */

#ifdef WANT_KRBTF

/* PRIVATE DEPENDENCIES */
/* globals */

/* privates */
static char tf_dir[] = PATH_SASLAUTHD_RUNDIR "/.tf";
static char *tfn_cookie = 0;
static int tfn_cookie_len = 0;
static char pidstring[80];
size_t pidstring_len = 0;
/* END PRIVATE DEPENDENCIES */

#endif /* WANT_KRBTF */

/* FUNCTION: krbtf_init */

/* SYNOPSIS
 * Initialize the Kerberos IV/V ticket-file/credential-cache common code
 *
 * When possible, use Heimdal krb5's memory-only credential caches--
 * this saves a whole bunch of useless disk i/o's to create and destroy
 * a file which we don't want anybody to see anyway.
 *
 * If not, this function will create a private directory for ticket
 * files and cache getpid() for later use.  Therefore, we must be
 * called AFTER main() does whatever fork()ing it wants.
 *
 * END SYNOPSIS */

int					/* R: -1 on failure, else 0 */
krbtf_init (
  /* PARAMETERS */
  void					/* no parameters */
  /* END PARAMETERS */
  )
{
#ifdef WANT_KRBTF
    /* VARIABLES */
    int rc;				/* return code holder */
    struct stat sb;			/* stat() work area */
    /* END VARIABLES */
    authmech_t *authmech;

#ifdef AUTH_KRB5
    for (authmech = mechanisms; authmech->name != NULL; authmech++ ) {
	    if (authmech->initialize != auth_krb5_init) continue;
	    /* This execution is using krb5 */
	    /* Both MIT krb5 and Heimdal support MEMORY: ccaches */
	    tfn_cookie = "MEMORY:0";
	    tfn_cookie_len = strlen(tfn_cookie);
	    return 0;
    }
#endif /* AUTH_KRB5 */

    if (((rc = mkdir(tf_dir, 0700)) == 0) || (errno == EEXIST)) {
	if ((rc = lstat(tf_dir, &sb)) == 0) {
	    if (sb.st_mode & S_IFLNK) {
		syslog(LOG_ERR, "krbtf_init: %s is a symbolic link", tf_dir);
		return -1;
	    }
	}
    }

    if (rc != 0) {
	syslog(LOG_ERR, "krbtf_init %s: %m", tf_dir);
	return -1;
    }

    /* cache getpid() for use in filenames */
    if ((pidstring_len = snprintf(pidstring, sizeof (pidstring), "%d", getpid())) >= sizeof (pidstring)) {
	    syslog(LOG_ERR, "krbtf_init pidstring too long(!?)");
	    return -1;
    }

    return 0;
#else /* WANT_KRBTF */
	syslog(LOG_ERR, "krbtf_init: not compiled!");
	return -1;
#endif /* WANT_KRBTF */
}

/* END FUNCTION: krbtf_init */

/* FUNCTION: krbtf_name */

/* SYNOPSIS
 * Spit a ticket-file/credentical-cache name into caller's array.
 *
 * If we can, emit the magic cookie for a memory-only krb5 ccname
 * END SYNOPSIS */

int					/* R: -1 on failure, else 0 */
krbtf_name (
  /* PARAMETERS */
  char *tfname,				/* O: where caller wants name */
  int len				/* I: available length */
  /* END PARAMETERS */
  )
{
#ifdef WANT_KRBTF
    if (tfn_cookie_len) {
	if (tfn_cookie_len + 1 > len) {
	    syslog(LOG_ERR, "krbtf_name: cookie name (%s) too long", tfn_cookie);
	    return -1;
	}
	strcpy(tfname, tfn_cookie);
    } else {
	int dir_len = sizeof (tf_dir) - 1; /* don't count the null */
	int want_len = dir_len + 1 + pidstring_len + 1;

	if (want_len > len) {
	    syslog(LOG_ERR, "krbtf_name: need room for %d bytes, got %d", want_len, len);
	    return -1;
	}

	strcpy(tfname, tf_dir);
	tfname += dir_len; len -= dir_len;

	*tfname++ = '/'; len--;

	strcpy(tfname, pidstring);

#ifdef SASLAUTHD_THREADED /* is this really used??? */
	tfname += pidstring_len;
	len -= pidstring_len;

	if (snprintf(tfname, len, "_%d", pthread_self() >= len)) {
	    syslog(LOG_ERR, "krbtf_name: no room for thread id");
	    return -1;
	}
#endif /* SASLAUTHD_THREADED */
    }

    return 0;
#else /* WANT_KRBTF */
	syslog(LOG_ERR, "krbtf_name: not compiled!");
	return -1;
#endif /* WANT_KRBTF */
}
/* END FUNCTION: krbtf_name */

/* END MODULE: krbtf */
