/*___INFO__MARK_BEGIN__*/ /************************************************************************* * * The Contents of this file are made available subject to the terms of * the Sun Industry Standards Source License Version 1.2 * * Sun Microsystems Inc., March, 2001 * * * Sun Industry Standards Source License Version 1.2 * ================================================= * The contents of this file are subject to the Sun Industry Standards * Source License Version 1.2 (the "License"); You may not use this file * except in compliance with the License. You may obtain a copy of the * License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html * * Software provided under this License is provided on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. * See the License for the specific provisions governing your rights and * obligations concerning the Software. * * The Initial Developer of the Original Code is: Sun Microsystems, Inc. * * Copyright: 2001 by Sun Microsystems, Inc. * * All Rights Reserved. * ************************************************************************/ /*___INFO__MARK_END__*/ #include #include #include #include #include #include #include #include #include #include #include #include "sge_all_listsL.h" #include "commlib.h" #include "sge_string.h" #include "sge.h" #include "sgermon.h" #include "sge_log.h" #include "sge_time.h" #include "sge_hostname.h" #include "uti/sge_stdlib.h" /* #include "sgermon.h" */ /* #include "basis_types.h" */ #include "sge_prog.h" #include "msg_krb.h" #include "sge_krbL.h" #include "krb5.h" /* Kerberos stuff */ #include "com_err.h" #include "krb_data.h" #include "krb_lib.h" /* NOTES We may need to tell the difference between a client getting a message back that was intended for a previous client and the client actually getting back a failed authentication message. This can be a problem because if a client goes down and another comes up, the new client may have the same commd address as the old client. This was handled in the existing SGE security code by using a connection ID which was assigned by the qmaster. The client compared the connection ID received from the qmaster in the message to his own connection ID. If the connection ID is different, he knew this message was for someone else. We could possibly handle this by using a unique identifier obtained from the KDC. Perhaps the session key encrypted in the server's key would suffice. Do we ever need to reconnect automatically as a client? May want to do double (reverse) lookup on hosts during initialization. */ #ifdef MIT_KRB extern krb5_cc_ops krb5_mcc_ops; #endif static krb_global_data_t gsd; /* global security data */ #define SGE_SERVICE "sge" #define KRB_KEYTAB "keytab" #define KRB_DEFAULT_IDLE_CLIENT_INTERVAL (15*60) #define KRB_USE_SAFE_MESSAGES #ifdef KRB_USE_SAFE_MESSAGES # define KRB_ENCRYPT krb5_mk_safe # define KRB_DECRYPT krb5_rd_safe #else # define KRB_ENCRYPT krb5_mk_priv # define KRB_DECRYPT krb5_rd_priv #endif static char *ptr2str(void *ptr, char *str); static void *str2ptr(const char *str); static int krb_send_auth_failure(char *tocommproc, int toid, char *tohost); static krb5_error_code krb_get_new_auth_con(krb5_auth_context *auth, krb5_rcache rcache); static krb5_error_code krb_get_tkt_for_daemon(const char *qmaster_host); /* static int krb_set_idle_client_interval(int interval); */ static int krb_delete_client(lListElem *client); #if 0 static int krb_set_idle_client_interval(int interval) { gsd.idle_client_interval = interval; return 0; } #endif static krb5_error_code krb_get_tkt_for_daemon(const char *qmaster_host) { krb5_error_code rc; char ccname[256]; DENTER(TOP_LAYER, "krb_get_tkt_for_daemon"); sprintf(ccname, "FILE:/tmp/krb5cc_%s", gsd.progname); krb5_free_cred_contents(gsd.context, &gsd.creds); memset(&gsd.creds, 0, sizeof(gsd.creds)); if ((rc = krb5_cc_resolve(gsd.context, ccname, &gsd.ccdef))) goto error; if ((rc = krb5_sname_to_principal(gsd.context, qmaster_host, SGE_SERVICE, KRB5_NT_SRV_HST, &gsd.creds.server))) goto error; if ((rc = krb5_sname_to_principal(gsd.context, gsd.hostname, gsd.progname, KRB5_NT_SRV_HST, &gsd.creds.client))) goto error; if ((rc = krb5_cc_initialize(gsd.context, gsd.ccdef, gsd.creds.client))) goto error; /* get SGE ticket using keytab */ if ((rc = krb5_get_in_tkt_with_keytab(gsd.context, 0, 0, NULL, NULL, gsd.keytab, gsd.ccdef, &gsd.creds, 0))) goto error; gsd.tgt_acquired = 1; error: DEXIT; return rc; } int krb_init(const char *progname) { int rc; char keytab[256]; struct hostent *he; u_long32 prog_number = uti_state_get_mewho(); DENTER(TOP_LAYER, "krb_init"); if (gsd.initialized) { DEXIT; return 0; } /* initialize kerberos context */ if ((rc = krb5_init_context(&gsd.context))) { ERROR((SGE_EVENT, MSG_KRB_KRB5INITCONTEXTFAILEDX_S , error_message(rc))); DEXIT; exit(1); } /* register memory-based credentials cache */ if ((rc = krb5_cc_register(gsd.context, &krb5_mcc_ops, 0))) { ERROR((SGE_EVENT, MSG_KRB_KRB5CCREGISTERFAILEDX_S , error_message(rc))); DEXIT; exit(1); } strcpy(gsd.progname, progname); /* check to see if I am a daemon */ if(!strcmp(prognames[QMASTER], progname) || !strcmp(prognames[EXECD], progname) || !strcmp(prognames[SCHEDD], progname)) gsd.daemon = 1; else gsd.daemon = 0; gsd.conn_list = NULL; #ifdef notdef /* Build the SGE kerberos service name. The format of the service name is service/host@realm, where service is the name of the kerberized service (sge), host is the where the service resides (qmaster host), and realm is the kerberos realm of the service (qmaster host realm). If the qmaster host name is not available we will build a name based on the local realm (i.e. sge/realm@realm) */ if ((krbhost = sge_get_master(getenv("SGE_CELL"), 0))) { char **realmlist=NULL; /* get kerberos realm of master host */ if ((rc = krb5_get_host_realm(gsd.context, krbhost, &realmlist))) { ERROR((SGE_EVENT, MSG_KRB_COULDNOTGETREALMFORQMASTERHOSTXY_SS , krbhost, error_message(rc))); } else { if (realmlist && realmlist[0]) krbrealm = sge_strdup(NULL, realmlist[0]); if (realmlist) if ((rc = krb5_free_host_realm(gsd.context, realmlist))) ERROR((SGE_EVENT, MSG_KRB_COULDNOTFREEREALMLISTX_S , error_message(rc))); } } /* if we don't have a host or realm, get the default realm */ if (!krbhost || !krbrealm) { if ((rc = krb5_get_default_realm(gsd.context, &krbrealm))) { ERROR((SGE_EVENT, MSG_KRB_COULDNOTGETDEFAULTREALMX_S , error_message(rc))); } else { krbhost = krbrealm; } } if (!krbhost || !krbrealm) { ERROR((SGE_EVENT, MSG_KRB_COULDNOTDETERMINEHOSTSORREALFORQMASTER )); DEXIT; exit(1); } sprintf(gsd.service, "%s/%s@%s", SGE_SERVICE, krbhost, krbrealm); if ((rc = krb5_parse_name(gsd.context, gsd.service, &gsd.clientp))) { ERROR((SGE_EVENT, MSG_KRB_KRB5PARSENAMEFAILEDX_S , error_message(rc))); if (!gsd.daemon) com_err(gsd.progname, rc, MSG_KRB_KRB5PARSENAMEFAILED ); DEXIT; exit(1); } #endif gsd.idle_client_interval = KRB_DEFAULT_IDLE_CLIENT_INTERVAL; if (gethostname(gsd.hostname, sizeof(gsd.hostname)) < 0) { ERROR((SGE_EVENT, MSG_KRB_GETHOSTNAMEFAILED)); DEXIT; exit(1); } if ((he = gethostbyname(gsd.hostname)) == NULL) { ERROR((SGE_EVENT, MSG_KRB_GETHOSTNAMEFAILED)); DEXIT; exit(1); } /* get local host address */ memcpy((struct in_addr *)&gsd.hostaddr, he->h_addr, sizeof(gsd.hostaddr)); /* if we are a SGE daemon, locate keytab file */ if (gsd.daemon) { /* set defaults for renewing TGTs */ gsd.tgt_renew_threshold = 60*60; /* renew TGTs if they expire in 1 hr */ gsd.tgt_renew_interval = 15*60; /* try to renew TGTs every 15 minutes */ /* use $SGE_ROOT/keytab */ sprintf(keytab, "FILE:%s/%s", sge_get_root_dir(0, NULL, 0, 1), KRB_KEYTAB); if ((rc = krb5_kt_resolve(gsd.context, keytab, &gsd.keytab))) { ERROR((SGE_EVENT, MSG_KRB_COULDNOTRESOLVEKEYTABX_S, error_message(rc))); DEXIT; exit(1); } if ((!strcmp(prognames[QMASTER], progname))) { if ((rc = krb5_sname_to_principal(gsd.context, NULL, SGE_SERVICE, KRB5_NT_SRV_HST, &gsd.serverp))) { ERROR((SGE_EVENT, MSG_KRB_KRB5SNAMETOPRINCIPALFAILEDX_S , error_message(rc))); com_err(gsd.progname, rc, MSG_KRB_KRB5SNAMETOPRINCIPAL ); DEXIT; exit(1); } } else { if ((rc = krb5_sname_to_principal(gsd.context, gsd.hostname, gsd.progname, KRB5_NT_SRV_HST, &gsd.serverp))) { ERROR((SGE_EVENT, MSG_KRB_KRB5SNAMETOPRINCIPALFAILEDX_S, error_message(rc))); com_err(gsd.progname, rc, MSG_KRB_KRB5SNAMETOPRINCIPAL ); DEXIT; exit(1); } } /* Read private key from keytab, if we can't read $SGE_ROOT/keytab, then try the default keytab */ if (((rc = krb5_kt_read_service_key(gsd.context, keytab, gsd.serverp, 0, ENCTYPE_DES_CBC_CRC, &gsd.daemon_key))) && ((rc != KRB5_KT_NOTFOUND && rc != ENOENT) || (((rc = krb5_kt_default_name(gsd.context, keytab, sizeof(keytab)-1))) || ((rc = krb5_kt_resolve(gsd.context, keytab, &gsd.keytab))) || ((rc = krb5_kt_read_service_key(gsd.context, keytab, gsd.serverp, 0, ENCTYPE_DES_CBC_CRC, &gsd.daemon_key)))))) { ERROR((SGE_EVENT, MSG_KRB_XCOULDNOTGETDAEMONKEYY_SS , gsd.progname, error_message(rc))); exit(1); } } /* do qmaster initialization */ if (gsd.daemon && (!strcmp(prognames[QMASTER], progname) && prog_number == QMASTER)) { /* NOTE: make sure we are REALLY the qmaster */ gsd.qmaster = 1; /* initialize the connection list */ gsd.conn_list = lCreateList("krb_connections", KRB_Type); if (gsd.conn_list == NULL) { ERROR(( SGE_EVENT, MSB_KRB_CONNECTIONLISTCOULDNOTBECREATED )); DEXIT; exit(1); } #ifdef notdef if ((rc = krb_get_tkt_for_daemon(gsd.hostname))) { ERROR((SGE_EVENT, MSG_KRB_XCOULDNOTGETSGETICKETUSINGKEYTABY_SS , gsd.progname, error_message(rc))); exit(1); } #endif } else { /* initialize client auth_context */ if ((rc = krb_get_new_auth_con(&gsd.auth_context, NULL))) { ERROR((SGE_EVENT, MSG_KRB_KRBGETNEWAUTHCONFAILUREX_S , error_message(rc))); com_err(gsd.progname, rc, MSG_KRB_KRBGETNEWAUTHCONFAILED ); DEXIT; exit(1); } if (gsd.daemon) { /* initialize the connection list in execd */ if (!strcmp(prognames[EXECD], progname)) { gsd.conn_list = lCreateList("krb_connections", KRB_Type); if (gsd.conn_list == NULL) { ERROR((SGE_EVENT, MSB_KRB_CONNECTIONLISTCOULDNOTBECREATED)); DEXIT; exit(1); } gsd.client = lCreateElem(KRB_Type); } /* get TGT from keytab */ /* get service ticket for SGE */ } else { /* regular client */ /* get credentials cache name */ if ((rc = krb5_cc_default(gsd.context, &gsd.ccdef))) { ERROR((SGE_EVENT, MSG_KRB_KRB5CCDEFAULTFAILEDX_S , error_message(rc))); com_err(gsd.progname, rc, MSG_KRB_COULDNOTGETCLIENTCREDENTIALS); DEXIT; exit(1); } if ((rc = krb5_cc_get_principal(gsd.context, gsd.ccdef, &gsd.clientp))) { ERROR((SGE_EVENT, MSG_KRB_KRB5CCGETPRINCIPALFAILEDX_S , error_message(rc))); com_err(gsd.progname, rc, MSG_KRB_COULDNOTGETCLIENTPRINCIPAL ); DEXIT; exit(1); } } } gsd.initialized = 1; DPRINTF(("====================[ KRB SECURITY ]===========================\n")); #ifdef notdef if (krbrealm) free(krbrealm); #endif DEXIT; return 0; } static int krb_delete_client(lListElem *client) { krb5_auth_context auth; lListElem *tgt_ep; krb5_creds ** creds; lDechainElem(gsd.conn_list, client); auth = (krb5_auth_context)str2ptr(lGetString(client, KRB_auth_context)); if (auth) { krb5_rcache rcache=NULL; if (!krb5_auth_con_getrcache(gsd.context, auth, &rcache)) if (rcache) krb5_auth_con_setrcache(gsd.context, auth, NULL); krb5_auth_con_free(gsd.context, auth); } /* free the TGTs */ for_each(tgt_ep, lGetList(client, KRB_tgt_list)) if ((creds = (krb5_creds **)str2ptr(lGetString(tgt_ep, KTGT_tgt)))) krb5_free_tgt_creds(gsd.context, creds); lFreeElem(client); return 0; } int krb_check_for_idle_clients(void) { static u_long32 next_time = 0; u_long32 now = sge_get_gmt(); lListElem *client, *next; DENTER(TOP_LAYER, "krb_check_for_idle_clients"); if ((now = sge_get_gmt()) (lGetUlong(client, KRB_timestamp) + KRB_CLIENT_TIMEOUT)) { krb_delete_client(client); } } next_time = now + gsd.idle_client_interval; DEXIT; return 0; } static krb5_error_code krb_get_new_auth_con(krb5_auth_context *auth, krb5_rcache rcache) { krb5_error_code rc=0; krb5_auth_context new_auth=NULL, old_auth=NULL; krb5_int32 flags; /* init new auth context */ if ((rc = krb5_auth_con_init(gsd.context, &new_auth))) goto error; if ((rc = krb5_auth_con_setrcache(gsd.context, new_auth, rcache))) goto error; #ifndef KRB_DO_REPLAY_STUFF /* turn off replay in auth_context */ if (((rc = krb5_auth_con_getflags(gsd.context, new_auth, &flags))) || ((rc = krb5_auth_con_setflags(gsd.context, new_auth, flags & ~KRB5_AUTH_CONTEXT_DO_TIME)))) goto error; #endif /* KRB_DO_REPLAY_STUFF */ /* replace old auth_context with new and free the old */ old_auth = *auth; *auth = new_auth; if (old_auth) { krb5_rcache old_rcache=NULL; if (!krb5_auth_con_getrcache(gsd.context, old_auth, &old_rcache)) if (old_rcache && rcache && old_rcache == rcache) krb5_auth_con_setrcache(gsd.context, old_auth, NULL); if ((rc = krb5_auth_con_free(gsd.context, old_auth))) { goto error; } } error: return rc; } static krb5_error_code krb_get_forwardable_tgt(char *host, krb5_auth_context auth, krb5_creds **in_creds, krb5_data *tgtbuf) { krb5_error_code rc = 0; char ccname[40]; krb5_ccache ccache = NULL; DENTER(TOP_LAYER, "krb_get_forwardable_tgt"); /* create an in-memory credentials cache */ strcpy(ccname, "MEMORY:xxxx"); if ((rc = krb5_cc_resolve(gsd.context, ccname, &ccache))) goto error; if ((rc = krb5_cc_initialize(gsd.context, ccache, (*in_creds)->client))) goto error; if ((rc = krb5_cc_store_cred(gsd.context, ccache, *in_creds))) goto error; if ((rc = krb5_fwd_tgt_creds(gsd.context, auth, host, (*in_creds)->client, NULL, ccache, 1, tgtbuf))) goto error; error: if (ccache) krb5_cc_destroy(gsd.context, ccache); DEXIT; return rc; } int krb_send_message(int synchron, const char *tocomproc, int toid, const char *tohost, int tag, char *buffer, int buflen, u_long32 *mid) { krb5_error_code rc; krb5_timestamp time_now; int ret = SEC_SEND_FAILED; krb5_auth_context auth; lListElem *client=NULL, *tgt_ep; lCondition *where=NULL; krb5_data ap_req, inbuf, outbuf, tgtbuf; krb5_address addr; sge_pack_buffer pb; krb5_principal server = NULL; int I_am_a_client = 0; krb5_creds **tgt_creds; #ifdef KRB_DO_REPLAY_STUFF krb5_address *portlocal_addr; krb5_rcache rcache; char *cp; #endif /* KRB_DO_REPLAY_STUFF */ const char *progname = uti_state_get_sge_formal_prog_name(); DENTER(TOP_LAYER, "krb_send_message"); /* prepare packing buffer */ if ((ret = init_packbuffer(&pb, 4096, 0))) { ERROR((SGE_EVENT, MSG_KRB_INITPACKBUFFERFAILED_S, cull_pack_strerror(ret))); goto error; } if (!gsd.initialized) { ERROR((SGE_EVENT,MSG_KRB_CALLERDIDNOTCALLKRBINIT )); goto error; } if ((rc = krb5_timeofday(gsd.context, &time_now))) { ERROR((SGE_EVENT, MSG_KRB_KRB5TIMEOFDAYFAILEDX_S, error_message(rc))); goto error; } ap_req.length = 0; inbuf.length = 0; outbuf.length = 0; tgtbuf.length = 0; /* if we are acting as a server, look up auth_context for the client using the commd tuple */ if (gsd.qmaster || (gsd.daemon && strcmp(prognames[QMASTER], tocomproc))) { if (toid) where = lWhere("%T(%I==%s && %I==%s && %I==%u)", KRB_Type, KRB_host, tohost, KRB_commproc, tocomproc, KRB_id, toid); else where = lWhere("%T(%I==%s && %I==%s)", KRB_Type, KRB_host, tohost, KRB_commproc, tocomproc); client = lFindFirst(gsd.conn_list, where); if (!client || !where) { ERROR((SGE_EVENT, MSG_KRB_NOCLIENTENTRYFOR_SSI , tohost, tocomproc, toid)); goto error; } /* set activity time stamp in client */ lSetUlong(client, KRB_timestamp, sge_get_gmt()); auth = (krb5_auth_context)str2ptr(lGetString(client, KRB_auth_context)); } else { const char *service; /* we are a client, we need to get a fresh auth_context */ I_am_a_client = 1; if ((rc = krb_get_new_auth_con(&gsd.auth_context, NULL))) { ERROR((SGE_EVENT, MSG_KRB_KRBGETNEWAUTHCONFAILUREX_S, error_message(rc))); com_err(gsd.progname, rc, MSG_KRB_COULDNOTGETNEWAUTHCONTEXT); goto error; } auth = gsd.auth_context; /* if we are a SGE daemon sending a message to the qmaster and we don't have a valid SGE ticket, go get one. Not having a valid ticket is due to either 1) we haven't got one yet, 2) the ticket is about to expire, or 3) the qmaster has moved to different host (i.e. the shadow) */ if (gsd.daemon && !strcmp(prognames[QMASTER], tocomproc) && (!gsd.tgt_acquired || gsd.creds.times.endtime < (time_now + gsd.tgt_renew_threshold) || (gsd.qmaster_host[0] && sge_hostcmp(tohost, gsd.qmaster_host)))) { strcpy(gsd.qmaster_host, tohost); if ((rc = krb_get_tkt_for_daemon(tohost))) { ERROR((SGE_EVENT, MSG_KRB_XCOULDNOTGETSGETICKETUSINGKEYTABY_SS, gsd.progname, error_message(rc))); exit(1); } } /* build AP_REQ */ inbuf.data = (char *) tohost; inbuf.length = strlen(tohost); /* Use SGE service for msgs to qmaster, daemon name for msgs to other daemons */ if (!strcmp(prognames[QMASTER], tocomproc)) service = SGE_SERVICE; else service = tocomproc; if ((rc = krb5_mk_req(gsd.context, &auth, 0, (char *)service, (char *) tohost, &inbuf, gsd.ccdef, &ap_req))) { ERROR((SGE_EVENT, MSG_KRB_FAILEDCREATINGAP_REQFORWXZY_SSIS, tohost, tocomproc, toid, error_message(rc))); if (!gsd.daemon) com_err(gsd.progname, rc, MSG_KRB_COULDNOTCREATEAUTHENTICATIONINFO); goto error; } } #ifdef KRB_PORT_STUFF addr.addrtype = ADDRTYPE_IPPORT; addr.length = sizeof(c_sock.sin_port); addr.contents = (krb5_octet *)&c_sock.sin_port; if ((rc = krb5_auth_con_setports(gsd.context, auth, &addr, NULL))) { ERROR((SGE_EVENT, MSG_KRB_KRB5AUTHCONSETADDRSFIALEDFORWXYZ_SSIS, tohost, tocomproc, toid, error_message(rc))); if (!gsd.daemon) com_err(gsd.progname, rc, MSG_KRB_COULDNOTSETPORTSINAUTHCONTEXT); goto error; } #endif addr.addrtype = ADDRTYPE_INET; addr.length = sizeof(gsd.hostaddr); addr.contents = (krb5_octet *)&gsd.hostaddr; if ((rc = krb5_auth_con_setaddrs(gsd.context, auth, &addr, NULL))) { ERROR((SGE_EVENT, MSG_KRB_KRB5AUTHCONSETADDRSFIALEDFORWXYZ_SSIS, tohost, tocomproc, toid, error_message(rc))); if (!gsd.daemon) com_err(gsd.progname, rc, MSG_KRB_COULDNOTSETADDRESSESINAUTHCONTEXT); goto error; } #ifdef KRB_DO_REPLAY_STUFF if ((rc = krb5_gen_portaddr(gsd.context, &addr, (krb5_pointer) &c_sock.sin_port, &portlocal_addr))) { ERROR((SGE_EVENT, MSG_KRB_KRB5GENPORTADDRFAILEDFORWXYZ_SSIS, tohost, tocomproc, toid, error_message(rc))); if (!gsd.daemon) com_err(gsd.progname, rc, MSG_KRB_COULDNOTGENPORTADDR ); goto error; } if ((rc = krb5_gen_replay_name(gsd.context, portlocal_addr, progname ? progname : "sge_client", &cp))) { ERROR((SGE_EVENT, MSG_KRB_KRB5GENREPLAYNAMEFAILEDFORWXYZ_SSIS , tohost, tocomproc, toid, error_message(rc))); if (!gsd.daemon) com_err(gsd.progname, rc, MSG_KRB_COULDNOTGENREPLAYNAME ); goto error; } rcache_name.length = strlen(cp); rcache_name.data = cp; if ((rc = krb5_get_server_rcache(gsd.context, &rcache_name, &rcache))) { ERROR((SGE_EVENT, MSG_KRB_KRB5GETSERVERRCACHEFAILEDFORWXYZ_SSIS , tohost, tocomproc, toid, error_message(rc))); if (!gsd.daemon) com_err(gsd.progname, rc, MSG_KRB_COULDNOTGETREPLAYCACHE); goto error; } /* free existing auth_context rcache */ krb5_auth_con_getrache(gsd.context, auth, &old_rcache); if (old_rcache) krb5_rc_close(gsd.context, old_rcache); /* set auth_context rcache */ krb5_auth_con_setrcache(gsd.context, auth, rcache); #endif /* KRB_DO_REPLAY_STUFF */ /* encrypt message */ inbuf.data = buffer; inbuf.length = buflen; if ((rc = KRB_ENCRYPT(gsd.context, auth, &inbuf, &outbuf, NULL))) { ERROR((SGE_EVENT, MSG_KRB_FAILEDENCRYPTINGMSGFORWXYZ_SSIS , tohost, tocomproc, toid, error_message(rc))); if (!gsd.daemon) com_err(gsd.progname, rc, MSG_KRB_FAILEDENCRYPTINGMESSAGE ); goto error; } /* get forwardable TGT for client */ if (I_am_a_client && gsd.client_flags & KRB_FORWARD_TGT) { if ((rc = krb5_sname_to_principal(gsd.context, NULL, SGE_SERVICE, KRB5_NT_SRV_HST, &server))) { ERROR((SGE_EVENT, MSG_KRB_KRB5SNAMETOPRINCIPALFAILEDX_S, error_message(rc))); goto error; } if ((rc = krb5_fwd_tgt_creds(gsd.context, auth, (char*) tohost, gsd.clientp, server, gsd.ccdef, 1, &tgtbuf))) { ERROR((SGE_EVENT, MSG_KRB_COULDNOTGETFORWARDABLETGTFORWXYZ_SSIS , tohost, tocomproc, toid, error_message(rc))); if (!gsd.daemon) com_err(gsd.progname, rc, MSG_KRB_UNABLETOFORWARDTGT ); } } else if (gsd.qmaster && !strcmp(prognames[EXECD], tocomproc) && gsd.tgt_id && client && ((tgt_ep = lGetElemUlong(lGetList(client, KRB_tgt_list), KTGT_id, gsd.tgt_id))) && ((tgt_creds = (krb5_creds **) str2ptr(lGetString(tgt_ep, KTGT_tgt))))) { /* get forwardable TGT for client for the execution host */ if ((rc = krb_get_forwardable_tgt((char*) tohost, auth, tgt_creds, &tgtbuf))) { ERROR((SGE_EVENT, MSG_KRB_COULDNOTGETFORWARDABLETGTFORWXYZ_SSIS , tohost, tocomproc, toid, error_message(rc))); } } /* pack AP_REQ, encrypted msg, and TGT together into a single message */ packint(&pb, ap_req.length); packint(&pb, outbuf.length); packint(&pb, tgtbuf.length); #if 1 /* * TODO EB: following code should be removed for the next version * where it is possible to change the packbuffer format */ /* unused int in pb; was previously used to store if data is compressed */ packint(&pb, -1); #endif if (ap_req.length) packbuf(&pb, ap_req.data, ap_req.length); if (outbuf.length) packbuf(&pb, outbuf.data, outbuf.length); if (tgtbuf.length) { packint(&pb, gsd.tgt_id); packbuf(&pb, tgtbuf.data, tgtbuf.length); } ret = send_message(synchron, tocomproc, toid, tohost, tag, pb.head_ptr, pb.bytes_used, mid, 0); error: if (server) krb5_free_principal(gsd.context, server); if (where) lFreeWhere(where); if (ap_req.length) krb5_xfree(ap_req.data); if (outbuf.length) krb5_xfree(outbuf.data); if (tgtbuf.length) krb5_xfree(tgtbuf.data); clear_packbuffer(&pb); DEXIT; return ret; } static int krb_unpackmsg(sge_pack_buffer *pb, krb5_data *buf1, krb5_data *buf2, krb5_data *buf3, u_long32 *tgt_id); static int krb_unpackmsg(sge_pack_buffer *pb, krb5_data *buf1, krb5_data *buf2, krb5_data *buf3, u_long32 *tgt_id) { int ret; u_long32 len1=0, len2=0, len3=0, cpr=0; *tgt_id = 0; if ((ret=unpackint(pb, &len1))) goto error; if ((ret=unpackint(pb, &len2))) goto error; if ((ret=unpackint(pb, &len3))) goto error; #if 1 /* * TODO EB: following code should be removed for the next version * where it is possible to change the packbuffer format */ if ((ret=unpackint(pb, &cpr))) goto error; #endif if ((buf1->length = len1)) { if ((ret=unpackbuf(pb, &buf1->data, buf1->length))) goto error; } else buf1->data = NULL; if ((buf2->length = len2)) { if ((ret=unpackbuf(pb, &buf2->data, buf2->length))) goto error; } else buf2->data = NULL; if ((buf3->length = len3)) { if ((ret=unpackint(pb, tgt_id))) goto error; if ((ret=unpackbuf(pb, &buf3->data, buf3->length))) goto error; } else buf3->data = NULL; ret = PACK_SUCCESS; error: return ret; } static int krb_send_auth_failure( char *tocommproc, int toid, char *tohost ) { int tag = TAG_AUTH_FAILURE; char buffer[BUFSIZ]; int buflen; sge_strlcpy(buffer, MSG_KRB_AUTHENTICATIONFAILURE, BUFSIZ); buflen = strlen(buffer); return send_message(0, tocommproc, toid, tohost, tag, buffer, buflen, NULL, 0); } int krb_receive_message(char *fromcommproc, u_short *fromid, char *fromhost, int *tag, char **buffer, u_long32 *buflen, int synchron) { krb5_error_code rc; int ret = SEC_RECEIVE_FAILED; krb5_auth_context auth=NULL; krb5_creds ** tgt_creds = NULL; lListElem *client=NULL; lCondition *where=NULL; krb5_data inbuf, outbuf, ap_req, tgtbuf; krb5_address addr; sge_pack_buffer pb; struct hostent *he; struct in_addr hostaddr; u_long32 tmplen=0; char tmpcommproc[MAXCOMPONENTLEN]; char tmphost[CL_MAXHOSTLEN]; u_short tmpid=0; int tmptag=0; u_long32 tgt_id=0; DENTER(TOP_LAYER,"krb_receive_message"); ap_req.length = 0; inbuf.length = 0; outbuf.length = 0; tgtbuf.length = 0; if (!gsd.initialized) { ERROR((SGE_EVENT,MSG_KRB_DIDNOTCALLKRBINIT )); ret = SEC_RECEIVE_FAILED; goto error; } /* receive message from commd */ if (fromcommproc) strcpy(tmpcommproc, fromcommproc); else tmpcommproc[0] = 0; if (fromid) tmpid = *fromid; if (fromhost) strcpy(tmphost, fromhost); else tmphost[0] = 0; if (tag) tmptag = *tag; ret = receive_message(tmpcommproc, &tmpid, tmphost, &tmptag, buffer, &tmplen, synchron); if (buflen) *buflen = tmplen; if (fromcommproc) strcpy(fromcommproc, tmpcommproc); if (fromhost) strcpy(fromhost, tmphost); if (fromid) *fromid = tmpid; if (tag) *tag = tmptag; if (ret) goto error; if (tmptag == TAG_AUTH_FAILURE) { if (gsd.qmaster) { ERROR((SGE_EVENT, MSG_KRB_INVALIDTAGAUTHFAILUREMSGRECEIVEDWXYZ_SSI , tmphost, tmpcommproc, (int) tmpid)); ret = SEC_RECEIVE_FAILED; goto error; } else if (gsd.daemon) { ERROR((SGE_EVENT, MSG_KRB_AUTHENTICATIONTOQMASTERFAILED)); gsd.reconnect = 1; ret = SEC_RECEIVE_FAILED; goto error; } else { ERROR((SGE_EVENT, MSG_KRB_AUTHENTICATIONTOQMASTERFAILED)); com_err(gsd.progname, rc, MSG_KRB_AUTHENTICATIONFAILED ); ret = SEC_RECEIVE_FAILED; goto error; } } /* unpack AP_REQ and encrypted message */ { int pack_ret = init_packbuffer_from_buffer(&pb, *buffer, tmplen); if(pack_ret != PACK_SUCCESS) { ERROR((SGE_EVENT, MSG_KRB_INITPACKBUFFERFAILED_S, cull_pack_strerror(pack_ret))); ret = SEC_RECEIVE_FAILED; goto error; } } if (krb_unpackmsg(&pb, &ap_req, &inbuf, &tgtbuf, &tgt_id)) { ERROR((SGE_EVENT, MSG_KRB_INVALIDMESSAGEUNPACKFAILURE )); if (gsd.qmaster) { if ((rc = krb_send_auth_failure(tmpcommproc, tmpid, tmphost))) { ERROR((SGE_EVENT, MSG_KRB_FAILEDSENDINGAUTH_FAILUREMESSAGEX_S, error_message(rc))); } } else { com_err(gsd.progname, rc, MSG_KRB_INVALIDMESSAGEPACKINGERROR ); gsd.reconnect = 1; } ret = SEC_RECEIVE_FAILED; goto error; } /* if we are the qmaster (or a SGE daemon acting as a server) */ if (gsd.qmaster || (gsd.daemon && strcmp(prognames[QMASTER], tmpcommproc))) { /* make sure we have an AP_REQ */ if (ap_req.length == 0) { ERROR((SGE_EVENT, MSG_KRB_INVALIDMESSAGENOAP_REQ )); if ((rc = krb_send_auth_failure(tmpcommproc, tmpid, tmphost))) { ERROR((SGE_EVENT, MSG_KRB_FAILEDSENDINGAUTH_FAILUREMESSAGEX_S , error_message(rc))); } ret = SEC_RECEIVE_FAILED; goto error; } /* lookup client in the connection list */ where = lWhere( "%T(%I==%s && %I==%s && %I==%u)", KRB_Type, KRB_host, tmphost, KRB_commproc, tmpcommproc, KRB_id, tmpid); client = lFindFirst(gsd.conn_list, where); /* Set up daemon replay cache */ if (gsd.rcache == NULL) { if ((rc = krb5_get_server_rcache(gsd.context, krb5_princ_component(gsd.context, gsd.serverp, 0), &gsd.rcache))) { ERROR((SGE_EVENT, MSG_KRB_XCOULDNOTCREATEREPLAYCACHEY_SS , gsd.progname, error_message(rc))); exit(1); } } /* if client is not in the list, add him */ if (!client || !where) { client = lCreateElem(KRB_Type); if (!client) { ERROR((SGE_EVENT, MSG_KRB_FAILEDCREATEOFCLIENT )); ret = SEC_RECEIVE_FAILED; goto error; } lSetHost(client, KRB_host, tmphost); lSetString(client, KRB_commproc, tmpcommproc); lSetUlong(client, KRB_id, tmpid); if ((rc = krb_get_new_auth_con(&auth, gsd.rcache))) { ERROR((SGE_EVENT, MSG_KRB_KRBGETNEWAUTHCONFAILUREX_S , error_message(rc))); ret = SEC_RECEIVE_FAILED; goto error; } else { lSetString(client, KRB_auth_context, ptr2str(auth, NULL)); } if ((ret=lAppendElem(gsd.conn_list, client))) { ERROR((SGE_EVENT, MSG_KRB_APPENDELEMFAILUREX_I , ret)); ret = SEC_RECEIVE_FAILED; goto error; } } else { /* get a fresh auth_context for the client */ auth = (krb5_auth_context)str2ptr(lGetString(client,KRB_auth_context)); if ((rc = krb_get_new_auth_con(&auth, gsd.rcache))) { ERROR((SGE_EVENT, MSG_KRB_KRBGETNEWAUTHCONFAILUREX_S , error_message(rc))); ret = SEC_RECEIVE_FAILED; goto error; } lSetString(client, KRB_auth_context, ptr2str(auth, NULL)); } /* set activity time stamp in client */ lSetUlong(client, KRB_timestamp, sge_get_gmt()); /* get auth_context from client entry */ auth = (krb5_auth_context)str2ptr(lGetString(client, KRB_auth_context)); /* authenticate the client using the AP_REQ */ if ((rc = krb5_rd_req(gsd.context, &auth, &ap_req, gsd.serverp, gsd.keytab, NULL, NULL))) { ERROR((SGE_EVENT, MSG_KRB_CLIENTWXYFAILEDAUTHENTICATIONZ_SSIS , tmphost, tmpcommproc, (int) tmpid, error_message(rc))); if ((rc = krb_send_auth_failure(tmpcommproc, tmpid, tmphost))) { ERROR((SGE_EVENT, MSG_KRB_FAILEDSENDINGAUTH_FAILUREMESSAGEX_S , error_message(rc))); } /* remove this client from the connection list to force reauthentication */ krb_delete_client(client); ret = SEC_RECEIVE_FAILED; goto error; } } else { /* make sure we do have no AP_REQ */ if (ap_req.length) { ERROR((SGE_EVENT, MSG_KRB_INVALIDMESSAGEHASANAP_REQ )); if (!gsd.daemon) com_err(gsd.progname, rc, MSG_KRB_INVALIDMESSAGERECEIVED ); ret = SEC_RECEIVE_FAILED; goto error; } /* use auth context from global data */ auth = gsd.auth_context; } /* put client's host address in the client auth_context */ if ((he = gethostbyname(tmphost)) == NULL) { ERROR((SGE_EVENT, MSG_KRB_GETHOSTBYNAMEFAILED)); ret = SEC_RECEIVE_FAILED; goto error; } memcpy((struct in_addr *)&hostaddr, he->h_addr, sizeof(hostaddr)); addr.addrtype = ADDRTYPE_INET; addr.length = sizeof(hostaddr); addr.contents = (krb5_octet *)&hostaddr; if ((rc = krb5_auth_con_setaddrs(gsd.context, auth, NULL, &addr))) { ERROR((SGE_EVENT, MSG_KRB_KRB5AUTHCONSETADDRSFAILEDFORWXYZ_SSIS , tmphost, tmpcommproc, (int) tmpid, error_message(rc))); ret = SEC_RECEIVE_FAILED; goto error; } /* handle forwarded TGT */ if (tgtbuf.length) { if ((rc = krb5_rd_cred(gsd.context, auth, &tgtbuf, &tgt_creds, NULL))) { ERROR((SGE_EVENT, MSG_KRB_UNABLETODECRYPTFORWARDEDTGTFORCLIENTWXYZ_SSIS , tmphost, tmpcommproc, (int)tmpid, error_message(rc))); } else { if (tgt_creds) { if ((gsd.qmaster && client) || (gsd.daemon && !strcmp(prognames[EXECD], gsd.progname))) { lListElem *tgt_ep; krb5_creds ** creds; lListElem *tmp_client = (gsd.qmaster && client) ? client : gsd.client; /* if this TGT already exists in the client's list, free it */ if ((tgt_ep = lGetElemUlong(lGetList(tmp_client, KRB_tgt_list), KTGT_id, tgt_id))) { creds = (krb5_creds **)str2ptr(lGetString(tgt_ep, KTGT_tgt)); if (creds) krb5_free_tgt_creds(gsd.context, creds); lFreeElem(lDechainElem(lGetList(tmp_client, KRB_tgt_list), tgt_ep)); } /* store TGT in list based on TGT ID, for later retrieval */ if ((tgt_ep = lAddSubUlong(tmp_client, KTGT_id, tgt_id, KRB_tgt_list, KTGT_Type))) { lSetString(tgt_ep, KTGT_tgt, ptr2str(tgt_creds, NULL)); tgt_creds = NULL; } else /* this shouldn't happen */ ERROR((SGE_EVENT, MSG_KRB_FAILEDADDINGTGTWTOCLIENTSTGTLISTFORXYZ_ISSI, (int)tgt_id, tmphost, tmpcommproc, (int)tmpid)); #if 0 if (gsd.qmaster && client) { krb5_creds ** creds; /* for qmaster, save TGT in client entry */ creds = (krb5_creds **)str2ptr(lGetString(client, KRB_tgt)); if (creds) krb5_free_tgt_creds(gsd.context, creds); lSetString(client, KRB_tgt, ptr2str(tgt_creds, NULL)); tgt_creds = NULL; } else if (gsd.daemon && !strcmp(prognames[EXECD], gsd.progname)) { /* for sge_execd, save TGT in gsd structure */ if (gsd.tgt_creds) krb5_free_tgt_creds(gsd.context, gsd.tgt_creds); gsd.tgt_creds = tgt_creds; tgt_creds = NULL; } #endif } else { ERROR((SGE_EVENT, MSG_KRB_ILLOGICALFORWARDABLETGTRECEIVEDFROMXYZ_SSI, tmphost, tmpcommproc, (int) tmpid)); } } } } /* decrypt message using auth_context */ if ((rc = KRB_DECRYPT(gsd.context, auth, &inbuf, &outbuf, NULL))) { ERROR((SGE_EVENT, MSG_KRB_FAILEDDECRYPTINGMSGFORWXYZ_SSIS , tmphost, tmpcommproc, (int) tmpid, error_message(rc))); if (gsd.qmaster || (gsd.daemon && strcmp(prognames[QMASTER], tmpcommproc))) { if ((rc = krb_send_auth_failure(tmpcommproc, tmpid, tmphost))) { ERROR((SGE_EVENT, MSG_KRB_FAILEDSENDINGAUTH_FAILUREMESSAGE_S, error_message(rc))); } } ret = SEC_RECEIVE_FAILED; goto error; } /* fill in caller's buffer and buflen */ if (buflen) *buflen = outbuf.length; if (*buffer) { outbuf.length=0; free(*buffer); *buffer = outbuf.data; /* otherwise buffer gets freed again by clear_packbuffer */ pb.head_ptr = NULL; } error: /* clean up */ if (where) lFreeWhere(where); if (ap_req.length) krb5_xfree(ap_req.data); if (inbuf.length) krb5_xfree(inbuf.data); if (outbuf.length) krb5_xfree(outbuf.data); if (tgtbuf.length) krb5_xfree(tgtbuf.data); if (tgt_creds) krb5_free_tgt_creds(gsd.context, tgt_creds); #ifdef notdef if (ticket) krb5_free_ticket(gsd.context, ticket); #endif clear_packbuffer(&pb); DEXIT; return ret; } int krb_verify_user( const char *host, const char *commproc, int id, const char *user ) { krb5_error_code rc; int ret=-1; lListElem *client=NULL; lCondition *where=NULL; krb5_auth_context auth=NULL; char clientname[256]; krb5_authenticator *authenticator=NULL; DENTER(TOP_LAYER,"krb_verify_user"); /* lookup client in the connection list using commd triple */ where = lWhere( "%T(%I==%s && %I==%s && %I==%u)", KRB_Type, KRB_host, host, KRB_commproc, commproc, KRB_id, id); client = lFindFirst(gsd.conn_list, where); if (!client) goto error; /* get client name from the client auth_context */ auth = (krb5_auth_context)str2ptr(lGetString(client, KRB_auth_context)); if (!auth) goto error; if ((rc = krb5_auth_con_getauthenticator(gsd.context, auth, &authenticator))) { ERROR((SGE_EVENT, MSG_KRB_KRB5AUTHCONGETAUTHENTICATORFAILEDFORWXYZ_SSIS, host, commproc, id, error_message(rc))); goto error; } { char *dat; int len; dat = krb5_princ_component(gsd.context, authenticator->client, 0)->data; len = krb5_princ_component(gsd.context, authenticator->client, 0)->length; strncpy(clientname, dat, len); clientname[len] = 0; } /* return comparison of passed-in user to client user */ if (strcmp(user, clientname)==0 || (strcmp(user, "root")==0 && (strcmp(prognames[EXECD], clientname)==0 || strcmp(prognames[SCHEDD], clientname)==0))) { ret = 0; } error: if (where) lFreeWhere(where); #ifdef notdef if (clientname) free(clientname); #endif if (authenticator) krb5_free_authenticator(gsd.context, authenticator); DEXIT; return ret; } /* * * The ptr2str and str2ptr routines are ugly, but I need some structures * in a list and * * a) I don't want to write my own list routines, * b) I don't want to convert the entire structures into the list types * c) I don't have time to add a new BLOB (Bit Level OBject) type * to the cull routines * * We really need to do (c). I've looked at what it would take and I * think it would not be too difficult to support this, but I don't have * time at the moment. * */ static char* ptr2str(void *ptr, char *str) { if (str == NULL) str = malloc(sizeof(void *)*2+4); if (str == NULL || ptr == NULL) return NULL; sprintf(str, "%p", ptr); return str; } static void* str2ptr(const char *str) { void *ptr; if (str == NULL) return NULL; if (sscanf(str, "%p", &ptr) != 1) return NULL; return ptr; } char * krb_bin2str( void *data, int len, char *str ) { char *src, *dest, *end; char *ostr = str; int n; if (str == NULL) str = (char *)malloc(len*2+5); if (str == NULL || data == NULL) return NULL; src = data; dest = str; end = src+len; /* encode length as non-zero values in the first 4 bytes */ *dest++ = (len / (255*255*255))+1; *dest++ = (len / (255*255))+1; *dest++ = (len / 255)+1; *dest++ = (len % 255)+1; /* encode the rest of the binary data as non-zero bytes in the string */ while (src < end) { if (*src == 0) { *dest++ = 1; for(n=0; n<254 && srcclient))) goto all_done; if ((rc = krb5_cc_store_cred(gsd.context, ccache, *tgt_creds))) goto all_done; if ((rc = chown(ccname+5, uid, -1))) { struct stat statbuf; if (stat(ccname + 5, & statbuf) == 0) { if (statbuf.st_uid == uid) rc = 0; } } all_done: if (ccache) krb5_cc_close(gsd.context, ccache); if (rc) { ERROR((SGE_EVENT, MSG_KRB_FAILEDSTORINGFORWARDEDTGTFORUIDXJOBYZ_IIS , (int)uid, (int)jobid, error_message(rc))); ret = -1; } DEXIT; return ret; } /* * krb_destroy_forwarded_tgt - destroy the job's credentials cache */ int krb_destroy_forwarded_tgt( int jobid ) { int ret = 0; krb5_error_code rc = 0; char ccname[40]; krb5_ccache ccache = NULL; DENTER(TOP_LAYER, "krb_destory_fowarded_tgt"); krb_get_ccname(jobid, ccname); if ((rc = krb5_cc_resolve(gsd.context, ccname, &ccache))) goto all_done; if ((rc = krb5_cc_destroy(gsd.context, ccache))) goto all_done; all_done: if (rc && rc != KRB5_FCC_NOFILE) { ERROR((SGE_EVENT,MSG_KRB_FAILEDDELETINGTGTFORJOBXY_IS , jobid, error_message(rc))); ret = -1; } DEXIT; return ret; } /* * krb_get_ccname - return the name of the job's credentials cache */ char *krb_get_ccname( int jobid, char *ccname ) { if (ccname == NULL) { ccname = (char *)malloc(40); if (ccname == NULL) return NULL; } sprintf(ccname, "FILE:/tmp/krb5cc_sge%d", jobid); return ccname; } krb5_context krb_context(void) { return gsd.context; } krb_global_data_t *krb_gsd(void) { return &gsd; } void krb_set_tgt_id(u_long tgt_id) { gsd.tgt_id = tgt_id; }