/
niceSOFT
/
inetutils
Обзор
Документация
Войти
/
niceSOFT
/
inetutils
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
libtelnet/forward.c
677 строк
17 KB
Collin Funk
libtelnet: prefer lchown to chown
13 мар 2026, 09:31
13 мар 2026, 09:31
0038a35
Код
Авторство
О чём код?
/* Copyright (C) 1997-2026 Free Software Foundation, Inc. This file is part of GNU Inetutils. GNU Inetutils is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNU Inetutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see `http://www.gnu.org/licenses/'. */ /* * Copyright (c) 1993 * The Regents of the University of California. 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 University 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 REGENTS 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 REGENTS 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. */ /* General-purpose forwarding routines. These routines may be put into */ /* libkrb5.a to allow widespread use */ #include <config.h> #if defined KRB5 && defined FORWARD # include <stdio.h> # include <pwd.h> # include <netdb.h> # ifdef HAVE_KRB5_KRB5_H # include <krb5/krb5.h> # endif # ifdef HAVE_KRB5_ASN1_H # include <krb5/asn1.h> # endif # ifdef HAVE_KRB5_CRC_32_H # include <krb5/crc-32.h> # endif # ifdef HAVE_KRB5_LOS_PROTO_H # include <krb5/los-proto.h> # endif # ifdef HAVE_KRB5_EXT_PROTO_H # include <krb5/ext-proto.h> # endif # define KRB5_DEFAULT_LIFE 60*60*8 /* 8 hours */ /* helper function: convert flags to necessary KDC options */ # define flags2options(flags) (flags & KDC_TKT_COMMON_MASK) /* Get a TGT for use at the remote host */ krb5_error_code get_for_creds (etype, sumtype, rhost, client, enc_key, forwardable, outbuf) const krb5_enctype etype; const krb5_cksumtype sumtype; char *rhost; krb5_principal client; krb5_keyblock *enc_key; int forwardable; /* Should forwarded TGT also be forwardable? */ krb5_data *outbuf; { struct hostent *hp; krb5_address **addrs; krb5_error_code retval; krb5_data *scratch; krb5_kdc_rep *dec_rep; krb5_error *err_reply; krb5_response tgsrep; krb5_creds creds, tgt; krb5_ccache cc; krb5_flags kdcoptions; krb5_timestamp now; char *remote_host; char **hrealms; int i; if (!rhost || !(hp = gethostbyname (rhost))) return KRB5_ERR_BAD_HOSTNAME; remote_host = (char *) malloc (strlen (hp->h_name) + 1); if (!remote_host) return ENOMEM; strcpy (remote_host, hp->h_name); if (retval = krb5_get_host_realm (remote_host, &hrealms)) { free (remote_host); return retval; } if (!hrealms[0]) { free (remote_host); krb5_xfree (hrealms); return KRB5_ERR_HOST_REALM_UNKNOWN; } /* Count elements */ for (i = 0; hp->h_addr_list[i]; i++); addrs = (krb5_address **) malloc ((i + 1) * sizeof (*addrs)); if (!addrs) return ENOMEM; for (i = 0; hp->h_addr_list[i]; i++) { addrs[i] = (krb5_address *) malloc (sizeof (krb5_address)); if (addrs[i]) { addrs[i]->addrtype = hp->h_addrtype; addrs[i]->length = hp->h_length; addrs[i]->contents = (unsigned char *) malloc (addrs[i]->length); if (!addrs[i]->contents) { krb5_free_addresses (addrs); return ENOMEM; } else memmove ((char *) addrs[i]->contents, hp->h_addr_list[i], addrs[i]->length); } else { return ENOMEM; } } addrs[i] = 0; memset ((char *) &creds, 0, sizeof (creds)); if (retval = krb5_copy_principal (client, &creds.client)) return retval; if (retval = krb5_build_principal_ext (&creds.server, strlen (hrealms[0]), hrealms[0], KRB5_TGS_NAME_SIZE, KRB5_TGS_NAME, client->realm.length, client->realm.data, 0)) return retval; creds.times.starttime = 0; if (retval = krb5_timeofday (&now)) { return retval; } creds.times.endtime = now + KRB5_DEFAULT_LIFE; creds.times.renew_till = 0; if (retval = krb5_cc_default (&cc)) { return retval; } /* fetch tgt directly from cache */ if (retval = krb5_cc_retrieve_cred (cc, KRB5_TC_MATCH_SRV_NAMEONLY, &creds, &tgt)) { return retval; } /* tgt->client must be equal to creds.client */ if (!krb5_principal_compare (tgt.client, creds.client)) return KRB5_PRINC_NOMATCH; if (!tgt.ticket.length) return (KRB5_NO_TKT_SUPPLIED); kdcoptions = flags2options (tgt.ticket_flags) | KDC_OPT_FORWARDED; if (!forwardable) /* Reset KDC_OPT_FORWARDABLE */ kdcoptions &= ~(KDC_OPT_FORWARDABLE); if (retval = krb5_send_tgs (kdcoptions, &creds.times, etype, sumtype, creds.server, addrs, creds.authdata, 0, /* no padata */ 0, /* no second ticket */ &tgt, &tgsrep)) return retval; # undef cleanup # define cleanup() free(tgsrep.response.data) switch (tgsrep.message_type) { case KRB5_TGS_REP: break; case KRB5_ERROR: default: if (!krb5_is_krb_error (&tgsrep.response)) { retval = KRB5KRB_AP_ERR_MSG_TYPE; } else retval = decode_krb5_error (&tgsrep.response, &err_reply); if (retval) { cleanup (); return retval; /* neither proper reply nor error! */ } retval = err_reply->error + ERROR_TABLE_BASE_krb5; krb5_free_error (err_reply); cleanup (); return retval; } retval = krb5_decode_kdc_rep (&tgsrep.response, &tgt.keyblock, etype, /* enctype */ &dec_rep); cleanup (); if (retval) return retval; # undef cleanup # define cleanup() {\ memset((char *)dec_rep->enc_part2->session->contents, 0,\ dec_rep->enc_part2->session->length);\ krb5_free_kdc_rep(dec_rep); } if (dec_rep->msg_type != KRB5_TGS_REP) { retval = KRB5KRB_AP_ERR_MSG_TYPE; cleanup (); return retval; } /* now it's decrypted and ready for prime time */ if (!krb5_principal_compare (dec_rep->client, tgt.client)) { cleanup (); return KRB5_KDCREP_MODIFIED; } if (retval = mk_cred (dec_rep, etype, enc_key, 0, 0, outbuf)) return retval; krb5_free_kdc_rep (dec_rep); return retval; # undef cleanup } /* Create asn.1 encoded KRB-CRED message from the kdc reply. */ krb5_error_code mk_cred (dec_rep, etype, key, sender_addr, recv_addr, outbuf) krb5_kdc_rep *dec_rep; krb5_enctype etype; krb5_keyblock *key; krb5_address *sender_addr; krb5_address *recv_addr; krb5_data *outbuf; { krb5_error_code retval; krb5_encrypt_block eblock; krb5_cred ret_cred; krb5_cred_enc_part cred_enc_part; krb5_data *scratch; if (!valid_etype (etype)) return KRB5_PROG_ETYPE_NOSUPP; ret_cred.tickets = (krb5_ticket **) calloc (2, sizeof (*ret_cred.tickets)); if (!ret_cred.tickets) return ENOMEM; ret_cred.tickets[0] = dec_rep->ticket; ret_cred.tickets[1] = 0; ret_cred.enc_part.etype = etype; ret_cred.enc_part.kvno = 0; cred_enc_part.ticket_info = (krb5_cred_info **) calloc (2, sizeof (*cred_enc_part.ticket_info)); if (!cred_enc_part.ticket_info) { krb5_free_tickets (ret_cred.tickets); return ENOMEM; } cred_enc_part.ticket_info[0] = (krb5_cred_info *) malloc (sizeof (*cred_enc_part.ticket_info[0])); if (!cred_enc_part.ticket_info[0]) { krb5_free_tickets (ret_cred.tickets); krb5_free_cred_enc_part (cred_enc_part); return ENOMEM; } cred_enc_part.nonce = 0; if (retval = krb5_us_timeofday (&cred_enc_part.timestamp, &cred_enc_part.usec)) return retval; cred_enc_part.s_address = (krb5_address *) sender_addr; cred_enc_part.r_address = (krb5_address *) recv_addr; cred_enc_part.ticket_info[0]->session = dec_rep->enc_part2->session; cred_enc_part.ticket_info[0]->client = dec_rep->client; cred_enc_part.ticket_info[0]->server = dec_rep->enc_part2->server; cred_enc_part.ticket_info[0]->flags = dec_rep->enc_part2->flags; cred_enc_part.ticket_info[0]->times = dec_rep->enc_part2->times; cred_enc_part.ticket_info[0]->caddrs = dec_rep->enc_part2->caddrs; cred_enc_part.ticket_info[1] = 0; /* start by encoding to-be-encrypted part of the message */ if (retval = encode_krb5_enc_cred_part (&cred_enc_part, &scratch)) return retval; # define cleanup_scratch() { memset(scratch->data, 0, scratch->length); krb5_free_data(scratch); } /* put together an eblock for this encryption */ krb5_use_cstype (&eblock, etype); ret_cred.enc_part.ciphertext.length = krb5_encrypt_size (scratch->length, eblock.crypto_entry); /* add padding area, and zero it */ if (!(scratch->data = realloc (scratch->data, ret_cred.enc_part.ciphertext.length))) { /* may destroy scratch->data */ krb5_xfree (scratch); return ENOMEM; } memset (scratch->data + scratch->length, 0, ret_cred.enc_part.ciphertext.length - scratch->length); if (!(ret_cred.enc_part.ciphertext.data = malloc (ret_cred.enc_part.ciphertext.length))) { retval = ENOMEM; goto clean_scratch; } # define cleanup_encpart() {\ memset(ret_cred.enc_part.ciphertext.data, 0, \ ret_cred.enc_part.ciphertext.length); \ free(ret_cred.enc_part.ciphertext.data); \ ret_cred.enc_part.ciphertext.length = 0; \ ret_cred.enc_part.ciphertext.data = 0;} /* do any necessary key pre-processing */ if (retval = krb5_process_key (&eblock, key)) { goto clean_encpart; } # define cleanup_prockey() { krb5_finish_key(&eblock);} /* call the encryption routine */ if (retval = krb5_encrypt ((krb5_pointer) scratch->data, (krb5_pointer) ret_cred.enc_part.ciphertext.data, scratch->length, &eblock, 0)) { goto clean_prockey; } /* private message is now assembled-- do some cleanup */ cleanup_scratch (); if (retval = krb5_finish_key (&eblock)) { cleanup_encpart (); return retval; } /* encode private message */ if (retval = encode_krb5_cred (&ret_cred, &scratch)) { cleanup_encpart (); return retval; } cleanup_encpart (); *outbuf = *scratch; krb5_xfree (scratch); return 0; clean_prockey: cleanup_prockey (); clean_encpart: cleanup_encpart (); clean_scratch: cleanup_scratch (); return retval; # undef cleanup_prockey # undef cleanup_encpart # undef cleanup_scratch } /* Decode, decrypt and store the forwarded creds in the local ccache. */ krb5_error_code rd_and_store_for_creds (inbuf, ticket, lusername) krb5_data *inbuf; krb5_ticket *ticket; char *lusername; { krb5_encrypt_block eblock; krb5_creds creds; krb5_error_code retval; char ccname[35]; krb5_ccache ccache = NULL; struct passwd *pwd; if (retval = rd_cred (inbuf, ticket->enc_part2->session, &creds, 0, 0)) { return (retval); } if (!(pwd = (struct passwd *) getpwnam (lusername))) { return -1; } sprintf (ccname, "FILE:/tmp/krb5cc_%d", pwd->pw_uid); if (retval = krb5_cc_resolve (ccname, &ccache)) { return (retval); } if (retval = krb5_cc_initialize (ccache, ticket->enc_part2->client)) { return (retval); } if (retval = krb5_cc_store_cred (ccache, &creds)) { return (retval); } if (retval = lchown (ccname + 5, pwd->pw_uid, -1)) { return (retval); } return retval; } extern krb5_deltat krb5_clockskew; # define in_clock_skew(date) (abs((date)-currenttime) < krb5_clockskew) /* Decode the KRB-CRED message, and return creds */ krb5_error_code rd_cred (inbuf, key, creds, sender_addr, recv_addr) const krb5_data *inbuf; const krb5_keyblock *key; krb5_creds *creds; /* Filled in */ const krb5_address *sender_addr; /* optional */ const krb5_address *recv_addr; /* optional */ { krb5_error_code retval; krb5_encrypt_block eblock; krb5_cred *credmsg; krb5_cred_enc_part *credmsg_enc_part; krb5_data *scratch; krb5_timestamp currenttime; if (!krb5_is_krb_cred (inbuf)) return KRB5KRB_AP_ERR_MSG_TYPE; /* decode private message */ if (retval = decode_krb5_cred (inbuf, &credmsg)) { return retval; } # define cleanup_credmsg() {krb5_xfree(credmsg->enc_part.ciphertext.data); krb5_xfree(credmsg);} if (!(scratch = (krb5_data *) malloc (sizeof (*scratch)))) { cleanup_credmsg (); return ENOMEM; } # define cleanup_scratch() {memset(scratch->data, 0, scratch->length); krb5_xfree(scratch->data);} if (retval = encode_krb5_ticket (credmsg->tickets[0], &scratch)) { cleanup_credmsg (); cleanup_scratch (); return (retval); } creds->ticket = *scratch; if (!(creds->ticket.data = malloc (scratch->length))) { krb5_xfree (creds->ticket.data); return ENOMEM; } memmove ((char *) creds->ticket.data, (char *) scratch->data, scratch->length); cleanup_scratch (); if (!valid_etype (credmsg->enc_part.etype)) { cleanup_credmsg (); return KRB5_PROG_ETYPE_NOSUPP; } /* put together an eblock for this decryption */ krb5_use_cstype (&eblock, credmsg->enc_part.etype); scratch->length = credmsg->enc_part.ciphertext.length; if (!(scratch->data = malloc (scratch->length))) { cleanup_credmsg (); return ENOMEM; } /* do any necessary key pre-processing */ if (retval = krb5_process_key (&eblock, key)) { cleanup_credmsg (); cleanup_scratch (); return retval; } # define cleanup_prockey() { krb5_finish_key(&eblock);} /* call the decryption routine */ if (retval = krb5_decrypt ((krb5_pointer) credmsg->enc_part.ciphertext.data, (krb5_pointer) scratch->data, scratch->length, &eblock, 0)) { cleanup_credmsg (); cleanup_scratch (); cleanup_prockey (); return retval; } /* cred message is now decrypted -- do some cleanup */ cleanup_credmsg (); if (retval = krb5_finish_key (&eblock)) { cleanup_scratch (); return retval; } /* now decode the decrypted stuff */ if (retval = decode_krb5_enc_cred_part (scratch, &credmsg_enc_part)) { cleanup_scratch (); return retval; } cleanup_scratch (); # define cleanup_mesg() {krb5_xfree(credmsg_enc_part);} if (retval = krb5_timeofday (¤ttime)) { cleanup_mesg (); return retval; } if (!in_clock_skew (credmsg_enc_part->timestamp)) { cleanup_mesg (); return KRB5KRB_AP_ERR_SKEW; } if (sender_addr && credmsg_enc_part->s_address && !krb5_address_compare (sender_addr, credmsg_enc_part->s_address)) { cleanup_mesg (); return KRB5KRB_AP_ERR_BADADDR; } if (recv_addr && credmsg_enc_part->r_address && !krb5_address_compare (recv_addr, credmsg_enc_part->r_address)) { cleanup_mesg (); return KRB5KRB_AP_ERR_BADADDR; } if (credmsg_enc_part->r_address) { krb5_address **our_addrs; if (retval = krb5_os_localaddr (&our_addrs)) { cleanup_mesg (); return retval; } if (!krb5_address_search (credmsg_enc_part->r_address, our_addrs)) { krb5_free_addresses (our_addrs); cleanup_mesg (); return KRB5KRB_AP_ERR_BADADDR; } krb5_free_addresses (our_addrs); } if (retval = krb5_copy_principal (credmsg_enc_part->ticket_info[0]->client, &creds->client)) { return (retval); } if (retval = krb5_copy_principal (credmsg_enc_part->ticket_info[0]->server, &creds->server)) { return (retval); } if (retval = krb5_copy_keyblock_contents (credmsg_enc_part->ticket_info[0]->session, &creds->keyblock)) { return (retval); } # undef clean # define clean() {\ memset((char *)creds->keyblock.contents, 0, creds->keyblock.length);} creds->times = credmsg_enc_part->ticket_info[0]->times; creds->is_skey = FALSE; creds->ticket_flags = credmsg_enc_part->ticket_info[0]->flags; if (retval = krb5_copy_addresses (credmsg_enc_part->ticket_info[0]->caddrs, &creds->addresses)) { clean (); return (retval); } creds->second_ticket.length = 0; creds->authdata = 0; cleanup_mesg (); return 0; # undef clean # undef cleanup_credmsg # undef cleanup_scratch # undef cleanup_prockey # undef cleanup_mesg } #endif /* defined(KRB5) && defined(FORWARD) */