/* * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan * (Royal Institute of Technology, Stockholm, Sweden). * 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. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Header: /afs/pdc.kth.se/cluster/tegner/src/kdeposit/RCS/in.kdepositd.c,v 1.1 2015/03/27 15:51:51 dah Exp dah $ * */ #include "kdeposit.h" krb5_context context; char krb5_tkfile[MAXPATHLEN]; static char *progname = "in.kdepositd"; static char *depositdir; static char *keytab; char *service = KF_SERVICE; static char* usage = "kdepositd -d -k [-h ] [-?]\n\n" "-d directory to deposit credentials in\n" "-k keytab to use for the service (mandatory)\n" "-h to use when authenticating (uses gethostname by default)\n" "-? shows this help\n"; static int protocol_version; static krb5_boolean kfd_match_version(const void *arg, const char *version) { if(strcmp(version, KF_VERSION_1) == 0) { protocol_version = 1; return TRUE; } return FALSE; } static int receive_cred (int sock, const char *service, char *hostname) { krb5_auth_context auth_context; krb5_error_code status; krb5_principal server; krb5_ticket *ticket; char *name; char *longname; char ret_string[10]; krb5_data data; krb5_data tk_tag; krb5_ccache ccache; char ccname[MAXPATHLEN]; struct stat statres; krb5_address *remote_addr = NULL, *local_addr = NULL; krb5_keytab keytabid; status = krb5_init_context(&context); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error in krb5_init_context"); exit (1); } status = krb5_kt_resolve(context, keytab, &keytabid); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error in krb5_kt_resolve"); exit (1); } status = krb5_auth_con_init (context, &auth_context); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error in krb5_auth_con_init"); exit (1); } status = krb5_auth_con_setaddrs_from_fd (context, auth_context, &sock); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error in krb5_auth_con_setaddr"); exit (1); } status = krb5_auth_con_getaddrs(context, auth_context, &local_addr, &remote_addr); if (status) { syslog(LOG_ERR | LOG_AUTH, "kr5_auth_con_getaddrs"); exit (1); } char remote[256]; status = krb5_print_address(remote_addr, remote, sizeof(remote), NULL); if (status) { syslog(LOG_ERR | LOG_AUTH, "krb5_print_address"); exit (1); } status = krb5_sname_to_principal (context, hostname, service, KRB5_NT_SRV_HST, &server); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error in krb5_sname_to_principal"); exit (1); } status = krb5_recvauth_match_version (context, &auth_context, &sock, kfd_match_version, NULL, server, 0, keytabid, &ticket); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error in krb5_recvauth_match_version"); exit (1); } status = krb5_unparse_name (context, ticket->client, &longname); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error in krb5_unparse_name"); exit(1); } syslog(LOG_INFO | LOG_AUTH, "Connection by %s from %s", longname, remote); status = krb5_unparse_name_short (context, ticket->client, &name); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error in krb5_unparse_name"); exit(1); } krb5_data_zero (&tk_tag); status=krb5_read_priv_message (context, auth_context, &sock, &tk_tag); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error in krb5_read_message"); goto out; } if (tk_tag.length <= 1) { syslog(LOG_ERR | LOG_AUTH, "No tag supplied"); status = 1; goto out; } else { char* tag = (char*) tk_tag.data; // Terminate string. tag[tk_tag.length - 1] = '\0'; int i; // Check if tag is properly formed, only accept alpha-numericals. for (i = 0; i < tk_tag.length - 1; i++) { if (!isalnum(tag[i])) { syslog(LOG_ERR | LOG_AUTH, "Requested tag contains non-alpha-numerical token."); status = 1; goto out; } } snprintf (ccname, sizeof(ccname), "/%s/%s-%s", depositdir, name, tag); } krb5_data_zero (&data); status = krb5_read_priv_message(context, auth_context, &sock, &data); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error in krb5_read_priv_message"); goto out; } // Cache file should exist and be empty (being created on/by the // scheduling server earlier in the submission process). This is // the method used to make sure the forwarding request is relevant // to server - hence if the file does _not_ exist we reject the // request. // // Relax this check for now. /* if ((status = stat(ccname, &statres)) != 0) { */ /* syslog(LOG_ERR | LOG_AUTH, "Error stating requested credential cache: %s", ccname); */ /* goto out; */ /* } else if (statres.st_size != 0) { */ /* status = 1; */ /* syslog(LOG_ERR | LOG_AUTH, "Requested credential cache %s is not empty", ccname); */ /* goto out; */ /* } */ status = krb5_cc_resolve (context, ccname, &ccache); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error opening credential cache %s", ccname); goto out; } status = krb5_cc_initialize (context, ccache, ticket->client); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error initializing credential cache %s", ccname); goto out; } status = krb5_rd_cred2 (context, auth_context, ccache, &data); krb5_cc_close (context, ccache); if (status) { syslog(LOG_ERR | LOG_AUTH, "Error storing to credential cache %s", ccname); goto out; } strncpy(krb5_tkfile, ccname, sizeof(krb5_tkfile)); if (sizeof(krb5_tkfile) < strlen(ccname)) { krb5_tkfile[sizeof(krb5_tkfile) - 1] = '\0'; } else { krb5_tkfile[sizeof(strlen(ccname))] = '\0'; } syslog(LOG_NOTICE | LOG_AUTH, "%s forwarded ticket to %s", longname, ccname); out: if (status) { strncpy(ret_string, "no", sizeof(ret_string)); } else { strncpy(ret_string, "ok", sizeof(ret_string)); } data.data = ret_string; data.length = strlen(ret_string) + 1; status = krb5_write_priv_message(context, auth_context, &sock, &data); krb5_xfree(longname); krb5_xfree(name); krb5_data_free (&tk_tag); krb5_data_free (&data); krb5_auth_con_free(context, auth_context); return status; } int main(int argc, char **argv) { int ret; int c; char *hostname = NULL; if (argc > 0) { progname = argv[0]; } while ((c = getopt (argc, argv, "d:k:h:?")) != -1) { switch (c) { case '?': fprintf(stderr, "%s", usage); exit(0); break; case 'd': if ((depositdir = strdup(optarg)) == NULL) { perror(progname); exit(1); } break; case 'k': if ((keytab = strdup(optarg)) == NULL) { perror(progname); exit(1); } break; case 'h': if ((hostname = strdup(optarg)) == NULL) { perror(progname); exit(1); } break; default: fprintf(stderr, "Unable to parse command line\n\n%s", usage); exit(1); break; } } if (keytab == NULL || depositdir == NULL) { fprintf(stderr, "Unable to parse command line\n\n%s", usage); exit(1); } openlog (progname, LOG_ODELAY | LOG_PID, LOG_AUTHPRIV); // If intended hostname is provided on the command line - use that - otherwise what is returned from gethostname if (hostname == NULL) { if(gethostname (hostname, sizeof(hostname)) < 0) { syslog(LOG_ERR | LOG_AUTH, "Error in gethostname"); exit (1); } } ret = receive_cred(STDIN_FILENO, service, hostname); closelog(); return ret; }