Beispiel #1
0
krb5_error_code
kg_encrypt(krb5_context context, krb5_key key, int usage, krb5_pointer iv,
           krb5_const_pointer in, krb5_pointer out, unsigned int length)
{
    krb5_error_code code;
    size_t blocksize;
    krb5_data ivd, *pivd, inputd;
    krb5_enc_data outputd;

    if (iv) {
        code = krb5_c_block_size(context, key->keyblock.enctype, &blocksize);
        if (code)
            return(code);

        ivd.length = blocksize;
        ivd.data = malloc(ivd.length);
        if (ivd.data == NULL)
            return ENOMEM;
        memcpy(ivd.data, iv, ivd.length);
        pivd = &ivd;
    } else {
        pivd = NULL;
    }

    inputd.length = length;
    inputd.data = (char *)in;

    outputd.ciphertext.length = length;
    outputd.ciphertext.data = out;

    code = krb5_k_encrypt(context, key, usage, pivd, &inputd, &outputd);
    if (pivd != NULL)
        free(pivd->data);
    return code;
}
krb5_error_code KRB5_CALLCONV
krb5_decrypt(krb5_context context, krb5_const_pointer inptr,
	     krb5_pointer outptr, size_t size, krb5_encrypt_block *eblock,
	     krb5_pointer ivec)
{
    krb5_enc_data inputd;
    krb5_data outputd, ivecd;
    size_t blocksize;
    krb5_error_code ret;

    if (ivec) {
	if ((ret = krb5_c_block_size(context, eblock->key->enctype, &blocksize)))
	    return(ret);

	ivecd.length = blocksize;
	ivecd.data = ivec;
    }

    /* size is the length of the input ciphertext data */
    inputd.enctype = eblock->key->enctype;
    inputd.ciphertext.length = size;
    inputd.ciphertext.data = inptr;

    /* we don't really know how big this is, but the code tends to assume
       that the output buffer size should be the same as the input
       buffer size */
    outputd.length = size;
    outputd.data = outptr;

    return(krb5_c_decrypt(context, eblock->key, 0, ivec?&ivecd:0,
			  &inputd, &outputd));
}
krb5_error_code krb5_decrypt_data(krb5_context context, krb5_keyblock *key,
				  krb5_pointer ivec, krb5_enc_data *enc_data,
				  krb5_data *data)
{
    krb5_error_code ret;
    krb5_data ivecd;
    size_t blocksize;

    if (ivec) {
	if ((ret = krb5_c_block_size(context, key->enctype, &blocksize)))
	    return(ret);

	ivecd.length = blocksize;
	ivecd.data = ivec;
    }

    data->length = enc_data->ciphertext.length;
    if ((data->data = (char *) malloc(data->length)) == NULL)
	return(ENOMEM);

    if ((ret = krb5_c_decrypt(context, key, 0, ivec?&ivecd:0, enc_data, data)))
	free(data->data);

    return(0);
}
krb5_error_code KRB5_CALLCONV
krb5_encrypt(krb5_context context, krb5_const_pointer inptr,
	     krb5_pointer outptr, size_t size, krb5_encrypt_block *eblock,
	     krb5_pointer ivec)
{
    krb5_data inputd, ivecd;
    krb5_enc_data outputd;
    size_t blocksize, outlen;
    krb5_error_code ret;

    if (ivec) {
	if ((ret = krb5_c_block_size(context, eblock->key->enctype, &blocksize)))
	    return(ret);

	ivecd.length = blocksize;
	ivecd.data = ivec;
    }

    /* size is the length of the input cleartext data */
    inputd.length = size;
    inputd.data = inptr;

    /* The size of the output buffer isn't part of the old api.  Not too
       safe.  So, we assume here that it's big enough. */
    if ((ret = krb5_c_encrypt_length(context, eblock->key->enctype, size,
				     &outlen)))
	return(ret);

    outputd.ciphertext.length = outlen;
    outputd.ciphertext.data = outptr;

    return(krb5_c_encrypt(context, eblock->key, 0, ivec?&ivecd:0,
			  &inputd, &outputd));
}
krb5_error_code krb5_encrypt_data(krb5_context context, krb5_keyblock *key,
				  krb5_pointer ivec, krb5_data *data,
				  krb5_enc_data *enc_data)
{
    krb5_error_code ret;
    size_t enclen, blocksize;
    krb5_data ivecd;

    if ((ret = krb5_c_encrypt_length(context, key->enctype, data->length,
				     &enclen)))
	return(ret);

    if (ivec) {
	if ((ret = krb5_c_block_size(context, key->enctype, &blocksize)))
	    return(ret);

	ivecd.length = blocksize;
	ivecd.data = ivec;
    }

    enc_data->magic = KV5M_ENC_DATA;
    enc_data->kvno = 0;
    enc_data->enctype = key->enctype;
    enc_data->ciphertext.length = enclen;
    if ((enc_data->ciphertext.data = malloc(enclen)) == NULL)
	return(ENOMEM);

    if ((ret = krb5_c_encrypt(context, key, 0, ivec?&ivecd:0, data, enc_data)))
	free(enc_data->ciphertext.data);

    return(ret);
}
Beispiel #6
0
int
kg_confounder_size(krb5_context context, krb5_enctype enctype)
{
    krb5_error_code code;
    size_t blocksize;
    /* We special case rc4*/
    if (enctype == ENCTYPE_ARCFOUR_HMAC || enctype == ENCTYPE_ARCFOUR_HMAC_EXP)
        return 8;
    code = krb5_c_block_size(context, enctype, &blocksize);
    if (code)
        return(-1); /* XXX */

    return(blocksize);
}
Beispiel #7
0
krb5_error_code KRB5_CALLCONV
krb5_auth_con_initivector(krb5_context context, krb5_auth_context auth_context)
{
    krb5_error_code ret;
    krb5_enctype enctype;

    if (auth_context->key) {
        size_t blocksize;

        enctype = krb5_k_key_enctype(context, auth_context->key);
        if ((ret = krb5_c_block_size(context, enctype, &blocksize)))
            return(ret);
        if ((auth_context->i_vector = (krb5_pointer)calloc(1,blocksize))) {
            return 0;
        }
        return ENOMEM;
    }
    return EINVAL; /* XXX need an error for no keyblock */
}
Beispiel #8
0
krb5_error_code
kg_decrypt_iov(krb5_context context, int proto, int dce_style, size_t ec,
               size_t rrc, krb5_key key, int usage, krb5_pointer iv,
               gss_iov_buffer_desc *iov, int iov_count)
{
    krb5_error_code code;
    size_t blocksize;
    krb5_data ivd, *pivd;
    size_t kiov_count;
    krb5_crypto_iov *kiov;

    if (iv) {
        code = krb5_c_block_size(context, key->keyblock.enctype, &blocksize);
        if (code)
            return(code);

        ivd.length = blocksize;
        ivd.data = malloc(ivd.length);
        if (ivd.data == NULL)
            return ENOMEM;
        memcpy(ivd.data, iv, ivd.length);
        pivd = &ivd;
    } else {
        pivd = NULL;
    }

    code = kg_translate_iov(context, proto, dce_style, ec, rrc,
                            key->keyblock.enctype, iov, iov_count,
                            &kiov, &kiov_count);
    if (code == 0) {
        code = krb5_k_decrypt_iov(context, key, usage, pivd, kiov, kiov_count);
        free(kiov);
    }

    if (pivd != NULL)
        free(pivd->data);

    return code;
}
Beispiel #9
0
static void
doit(int f,
	struct sockaddr_storage *fromp,
	krb5_context krb_context,
	int encr_flag,
	krb5_keytab keytab)
{
	int p, t, on = 1;
	char c;
	char abuf[INET6_ADDRSTRLEN];
	struct sockaddr_in *sin;
	struct sockaddr_in6 *sin6;
	int fromplen;
	in_port_t port;
	struct termios tp;
	boolean_t bad_port;
	boolean_t no_name;
	char rhost_addra[INET6_ADDRSTRLEN];

	if (!(rlbuf = malloc(BUFSIZ))) {
		syslog(LOG_ERR, "rlbuf malloc failed\n");
		exit(EXIT_FAILURE);
	}
	(void) alarm(60);
	if (read(f, &c, 1) != 1 || c != 0) {
		syslog(LOG_ERR, "failed to receive protocol zero byte\n");
		exit(EXIT_FAILURE);
	}
	(void) alarm(0);
	if (fromp->ss_family == AF_INET) {
		sin = (struct sockaddr_in *)fromp;
		port = sin->sin_port = ntohs((ushort_t)sin->sin_port);
		fromplen = sizeof (struct sockaddr_in);

		if (!inet_ntop(AF_INET, &sin->sin_addr,
			    rhost_addra, sizeof (rhost_addra)))
			goto badconversion;
	} else if (fromp->ss_family == AF_INET6) {
		sin6 = (struct sockaddr_in6 *)fromp;
		port = sin6->sin6_port = ntohs((ushort_t)sin6->sin6_port);
		fromplen = sizeof (struct sockaddr_in6);

		if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
			struct in_addr ipv4_addr;

			IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr,
					    &ipv4_addr);
			if (!inet_ntop(AF_INET, &ipv4_addr, rhost_addra,
				    sizeof (rhost_addra)))
				goto badconversion;
		} else {
			if (!inet_ntop(AF_INET6, &sin6->sin6_addr,
				    rhost_addra, sizeof (rhost_addra)))
				goto badconversion;
		}
	} else {
		syslog(LOG_ERR, "unknown address family %d\n",
		    fromp->ss_family);
		fatal(f, "Permission denied");
	}

	/*
	 * Allow connections only from the "ephemeral" reserved
	 * ports(ports 512 - 1023) by checking the remote port
	 * because other utilities(e.g. in.ftpd) can be used to
	 * allow a unprivileged user to originate a connection
	 * from a privileged port and provide untrustworthy
	 * authentication.
	 */
	bad_port = (use_auth != KRB5_RECVAUTH_V5 &&
		    (port >= (in_port_t)IPPORT_RESERVED) ||
		    (port < (in_port_t)(IPPORT_RESERVED/2)));
	no_name = getnameinfo((const struct sockaddr *) fromp,
			    fromplen, hostname, sizeof (hostname),
			    NULL, 0, 0) != 0;

	if (no_name || bad_port) {
		(void) strlcpy(abuf, rhost_addra, sizeof (abuf));
		/* If no host name, use IP address for name later on. */
		if (no_name)
			(void) strlcpy(hostname, abuf, sizeof (hostname));
	}

	if (!no_name) {
		/*
		 * Even if getnameinfo() succeeded, we still have to check
		 * for spoofing.
		 */
		check_address("rlogind", fromp, sin, sin6, rhost_addra,
		    hostname, sizeof (hostname));
	}

	if (bad_port) {
		if (no_name)
			syslog(LOG_NOTICE,
			    "connection from %s - bad port\n",
			    abuf);
		else
			syslog(LOG_NOTICE,
			    "connection from %s(%s) - bad port\n",
			    hostname, abuf);
		fatal(f, "Permission denied");
	}

	if (use_auth == KRB5_RECVAUTH_V5) {
		do_krb_login(f, rhost_addra, hostname,
			    krb_context, encr_flag, keytab);
		if (krusername != NULL && strlen(krusername)) {
			/*
			 * Kerberos Authentication succeeded,
			 * so set the proper program name to use
			 * with pam (important during 'cleanup'
			 * routine later).
			 */
			pam_prog_name = KRB5_PROG_NAME;
		}
	}

	if (write(f, "", 1) != 1) {
		syslog(LOG_NOTICE,
		    "send of the zero byte(to %s) failed:"
		    " cannot start data transfer mode\n",
		    (no_name ? abuf : hostname));
		exit(EXIT_FAILURE);
	}
	if ((p = open("/dev/ptmx", O_RDWR)) == -1)
		fatalperror(f, "cannot open /dev/ptmx");
	if (grantpt(p) == -1)
		fatal(f, "could not grant slave pty");
	if (unlockpt(p) == -1)
		fatal(f, "could not unlock slave pty");
	if ((line = ptsname(p)) == NULL)
		fatal(f, "could not enable slave pty");
	if ((t = open(line, O_RDWR)) == -1)
		fatal(f, "could not open slave pty");
	if (ioctl(t, I_PUSH, "ptem") == -1)
		fatalperror(f, "ioctl I_PUSH ptem");
	if (ioctl(t, I_PUSH, "ldterm") == -1)
		fatalperror(f, "ioctl I_PUSH ldterm");
	if (ioctl(t, I_PUSH, "ttcompat") == -1)
		fatalperror(f, "ioctl I_PUSH ttcompat");
	/*
	 * POP the sockmod and push the rlmod module.
	 *
	 * Note that sockmod has to be removed since readstream assumes
	 * a "raw" TPI endpoint(e.g. it uses getmsg).
	 */
	if (removemod(f, "sockmod") < 0)
		fatalperror(f, "couldn't remove sockmod");

	if (encr_flag) {
		if (ioctl(f, I_PUSH, "cryptmod") < 0)
		    fatalperror(f, "ioctl I_PUSH rlmod");

	}

	if (ioctl(f, I_PUSH, "rlmod") < 0)
		fatalperror(f, "ioctl I_PUSH rlmod");

	if (encr_flag) {
		/*
		 * Make sure rlmod will pass unrecognized IOCTLs to cryptmod
		 */
		uchar_t passthru = 1;
		struct strioctl rlmodctl;

		rlmodctl.ic_cmd = CRYPTPASSTHRU;
		rlmodctl.ic_timout = -1;
		rlmodctl.ic_len = sizeof (uchar_t);
		rlmodctl.ic_dp = (char *)&passthru;

		if (ioctl(f, I_STR, &rlmodctl) < 0)
			fatal(f, "ioctl CRYPTPASSTHRU failed\n");
	}

	/*
	 * readstream will do a getmsg till it receives
	 * M_PROTO type T_DATA_REQ from rloginmodopen()
	 * indicating all data on the stream prior to pushing rlmod has
	 * been drained at the stream head.
	 */
	if ((nsize = readstream(f, rlbuf, BUFSIZ)) < 0)
		fatalperror(f, "readstream failed");
	/*
	 * Make sure the pty doesn't modify the strings passed
	 * to login as part of the "rlogin protocol."  The login
	 * program should set these flags to apropriate values
	 * after it has read the strings.
	 */
	if (ioctl(t, TCGETS, &tp) == -1)
		fatalperror(f, "ioctl TCGETS");
	tp.c_lflag &= ~(ECHO|ICANON);
	tp.c_oflag &= ~(XTABS|OCRNL);
	tp.c_iflag &= ~(IGNPAR|ICRNL);
	if (ioctl(t, TCSETS, &tp) == -1)
		fatalperror(f, "ioctl TCSETS");

	/*
	 * System V ptys allow the TIOC{SG}WINSZ ioctl to be
	 * issued on the master side of the pty.  Luckily, that's
	 * the only tty ioctl we need to do do, so we can close the
	 * slave side in the parent process after the fork.
	 */
	(void) ioctl(p, TIOCSWINSZ, &win);

	pid = fork();
	if (pid < 0)
		fatalperror(f, "fork");
	if (pid == 0) {
		int tt;
		struct utmpx ut;

		/* System V login expects a utmp entry to already be there */
		(void) memset(&ut, 0, sizeof (ut));
		(void) strncpy(ut.ut_user, ".rlogin", sizeof (ut.ut_user));
		(void) strncpy(ut.ut_line, line, sizeof (ut.ut_line));
		ut.ut_pid = getpid();
		ut.ut_id[0] = 'r';
		ut.ut_id[1] = (char)SC_WILDC;
		ut.ut_id[2] = (char)SC_WILDC;
		ut.ut_id[3] = (char)SC_WILDC;
		ut.ut_type = LOGIN_PROCESS;
		ut.ut_exit.e_termination = 0;
		ut.ut_exit.e_exit = 0;
		(void) time(&ut.ut_tv.tv_sec);
		if (makeutx(&ut) == NULL)
			syslog(LOG_INFO, "in.rlogind:\tmakeutx failed");

		/* controlling tty */
		if (setsid() == -1)
			fatalperror(f, "setsid");
		if ((tt = open(line, O_RDWR)) == -1)
			fatalperror(f, "could not re-open slave pty");

		if (close(p) == -1)
			fatalperror(f, "error closing pty master");
		if (close(t) == -1)
			fatalperror(f, "error closing pty slave"
				    " opened before session established");
		/*
		 * If this fails we may or may not be able to output an
		 * error message.
		 */
		if (close(f) == -1)
			fatalperror(f, "error closing deamon stdout");
		if (dup2(tt, STDIN_FILENO) == -1 ||
		    dup2(tt, STDOUT_FILENO) == -1 ||
		    dup2(tt, STDERR_FILENO) == -1)
			exit(EXIT_FAILURE);	/* Disaster! No stderr! */

		(void) close(tt);

		if (use_auth == KRB5_RECVAUTH_V5 &&
		    krusername != NULL && strlen(krusername)) {
			(void) execl(LOGIN_PROGRAM, "login",
				    "-d", line,
				    "-r", hostname,
				    "-u", krusername, /* KRB5 principal name */
				    "-s", pam_prog_name,
				    "-t", term,	/* Remote Terminal */
				    "-U", rusername,	/* Remote User */
				    "-R", KRB5_REPOSITORY_NAME,
				    lusername,  /* local user */
				    NULL);
		} else {
			(void) execl(LOGIN_PROGRAM, "login",
				"-d", line,
				"-r", hostname,
				NULL);
		}

		fatalperror(STDERR_FILENO, "/bin/login");
		/*NOTREACHED*/
	}
	(void) close(t);
	(void) ioctl(f, FIONBIO, &on);
	(void) ioctl(p, FIONBIO, &on);

	/*
	 * Must ignore SIGTTOU, otherwise we'll stop
	 * when we try and set slave pty's window shape
	 * (our controlling tty is the master pty).
	 * Likewise, we don't want any of the tty-generated
	 * signals from chars passing through.
	 */
	(void) sigset(SIGTSTP, SIG_IGN);
	(void) sigset(SIGINT, SIG_IGN);
	(void) sigset(SIGQUIT, SIG_IGN);
	(void) sigset(SIGTTOU, SIG_IGN);
	(void) sigset(SIGTTIN, SIG_IGN);
	(void) sigset(SIGCHLD, cleanup);
	(void) setpgrp();

	if (encr_flag) {
		krb5_data ivec, *ivptr;
		uint_t ivec_usage;
		stop_stream(f, CRYPT_ENCRYPT|CRYPT_DECRYPT);

		/*
		 * Configure the STREAMS crypto module.  For now,
		 * don't use any IV parameter.  KCMDV0.2 support
		 * will require the use of Initialization Vectors
		 * for both encrypt and decrypt modes.
		 */
		if (kcmd_protocol == KCMD_OLD_PROTOCOL) {
			if (session_key->enctype == ENCTYPE_DES_CBC_CRC) {
				/*
				 * This is gross but necessary for MIT compat.
				 */
				ivec.length = session_key->length;
				ivec.data = (char *)session_key->contents;
				ivec_usage = IVEC_REUSE;
				ivptr = &ivec;
			} else {
				ivptr = NULL; /* defaults to all 0's */
				ivec_usage = IVEC_NEVER;
			}
			/*
			 * configure both sides of stream together
			 * since they share the same IV.
			 * This is what makes the OLD KCMD protocol
			 * less secure than the newer one - Bad ivecs.
			 */
			if (configure_stream(f, session_key,
				CRYPT_ENCRYPT|CRYPT_DECRYPT,
				ivptr, ivec_usage) != 0)
				fatal(f, "Cannot initialize encryption -"
					" exiting.\n");
		} else {
			size_t blocksize;
			if (session_key->enctype == ENCTYPE_ARCFOUR_HMAC ||
			    session_key->enctype == ENCTYPE_ARCFOUR_HMAC_EXP) {
				if (configure_stream(f, session_key,
					CRYPT_ENCRYPT|CRYPT_DECRYPT,
					NULL, IVEC_NEVER) != 0)
					fatal(f,
					"Cannot initialize encryption -"
					" exiting.\n");
				goto startcrypto;
			}
			if (krb5_c_block_size(krb_context,
					    session_key->enctype,
					    &blocksize)) {
				syslog(LOG_ERR, "Cannot determine blocksize "
				    "for encryption type %d",
				    session_key->enctype);
				fatal(f, "Cannot determine blocksize "
				    "for encryption - exiting.\n");
			}
			ivec.data = (char *)malloc(blocksize);
			ivec.length = blocksize;
			if (ivec.data == NULL)
				fatal(f, "memory error - exiting\n");
			/*
			 * Following MIT convention -
			 *   encrypt IV = 0x01 x blocksize
			 *   decrypt IV = 0x00 x blocksize
			 *   ivec_usage = IVEC_ONETIME
			 *
			 * configure_stream separately for encrypt and
			 * decrypt because there are 2 different IVs.
			 *
			 * AES uses 0's for IV.
			 */
			if (session_key->enctype ==
				ENCTYPE_AES128_CTS_HMAC_SHA1_96 ||
			    session_key->enctype ==
				ENCTYPE_AES256_CTS_HMAC_SHA1_96)
				(void) memset(ivec.data, 0x00, blocksize);
			else
				(void) memset(ivec.data, 0x01, blocksize);
			if (configure_stream(f, session_key, CRYPT_ENCRYPT,
				&ivec, IVEC_ONETIME) != 0)
				fatal(f, "Cannot initialize encryption -"
					" exiting.\n");
			(void) memset(ivec.data, 0x00, blocksize);
			if (configure_stream(f, session_key, CRYPT_DECRYPT,
				&ivec, IVEC_ONETIME) != 0)
				fatal(f, "Cannot initialize encryption -"
					" exiting.\n");

			(void) free(ivec.data);
		}
startcrypto:
		start_stream(f, CRYPT_ENCRYPT);
		start_stream(f, CRYPT_DECRYPT);
	}
	protocol(f, p, encr_flag);
	cleanup(0);
	/*NOTREACHED*/

badconversion:
	fatalperror(f, "address conversion");
	/*NOTREACHED*/
}
Beispiel #10
0
/*ARGSUSED*/
static krb5_error_code
krb5_mk_priv_basic(krb5_context context, const krb5_data *userdata, const krb5_keyblock *keyblock, krb5_replay_data *replaydata, krb5_address *local_addr, krb5_address *remote_addr, krb5_pointer i_vector, krb5_data *outbuf)
{
    krb5_error_code 	retval;
    krb5_priv 		privmsg;
    krb5_priv_enc_part 	privmsg_enc_part;
    krb5_data 		*scratch1, *scratch2, ivdata;
    size_t		blocksize, enclen;

    privmsg.enc_part.kvno = 0;	/* XXX allow user-set? */
    privmsg.enc_part.enctype = keyblock->enctype; 

    privmsg_enc_part.user_data = *userdata;
    privmsg_enc_part.s_address = local_addr;
    privmsg_enc_part.r_address = remote_addr;

    /* We should check too make sure one exists. */
    privmsg_enc_part.timestamp  = replaydata->timestamp;
    privmsg_enc_part.usec 	= replaydata->usec;
    privmsg_enc_part.seq_number = replaydata->seq;

    /* start by encoding to-be-encrypted part of the message */
    if ((retval = encode_krb5_enc_priv_part(&privmsg_enc_part, &scratch1)))
	return retval;

    /* put together an eblock for this encryption */
    if ((retval = krb5_c_encrypt_length(context, keyblock->enctype,
					scratch1->length, &enclen)))
	goto clean_scratch;

    privmsg.enc_part.ciphertext.length = enclen;
    if (!(privmsg.enc_part.ciphertext.data =
	  malloc(privmsg.enc_part.ciphertext.length))) {
        retval = ENOMEM;
        goto clean_scratch;
    }

    /* call the encryption routine */
    if (i_vector) {
	if ((retval = krb5_c_block_size(context, keyblock->enctype,
					&blocksize)))
	    goto clean_encpart;

	ivdata.length = blocksize;
	ivdata.data = i_vector;
    }

    if ((retval = krb5_c_encrypt(context, keyblock,
				 KRB5_KEYUSAGE_KRB_PRIV_ENCPART,
				 i_vector?&ivdata:0,
				 scratch1, &privmsg.enc_part)))
	goto clean_encpart;

    if ((retval = encode_krb5_priv(&privmsg, &scratch2)))
        goto clean_encpart;

    *outbuf = *scratch2;
    krb5_xfree(scratch2);
    retval = 0;

clean_encpart:
    memset(privmsg.enc_part.ciphertext.data, 0, 
	   privmsg.enc_part.ciphertext.length); 
    free(privmsg.enc_part.ciphertext.data); 
    privmsg.enc_part.ciphertext.length = 0;
    privmsg.enc_part.ciphertext.data = 0;

clean_scratch:
    memset(scratch1->data, 0, scratch1->length);
    krb5_free_data(context, scratch1); 

    return retval;
}
Beispiel #11
0
int
ksm_rgenerate_out_msg(struct snmp_secmod_outgoing_params *parms)
{
    krb5_auth_context auth_context = NULL;
    krb5_error_code retcode;
    krb5_ccache     cc = NULL;
    int             retval = SNMPERR_SUCCESS;
    krb5_data       outdata, ivector;
    krb5_keyblock  *subkey = NULL;
#ifdef MIT_NEW_CRYPTO
    krb5_data       input;
    krb5_enc_data   output;
    unsigned int    numcksumtypes;
    krb5_cksumtype  *cksumtype_array;
#else                           /* MIT_NEW_CRYPTO */
    krb5_encrypt_block eblock;
#endif                          /* MIT_NEW_CRYPTO */
    size_t          blocksize, encrypted_length;
    unsigned char  *encrypted_data = NULL;
    int             zero = 0, i;
    u_char         *cksum_pointer, *endp = *parms->wholeMsg;
    krb5_cksumtype  cksumtype;
    krb5_checksum   pdu_checksum;
    u_char         **wholeMsg = parms->wholeMsg;
    size_t	   *offset = parms->wholeMsgOffset, seq_offset;
    struct ksm_secStateRef *ksm_state = (struct ksm_secStateRef *)
        parms->secStateRef;
    int rc;

    DEBUGMSGTL(("ksm", "Starting KSM processing\n"));

    outdata.length = 0;
    outdata.data = NULL;
    ivector.length = 0;
    ivector.data = NULL;
    pdu_checksum.contents = NULL;

    if (!ksm_state) {
        /*
         * If we don't have a ksm_state, then we're a request.  Get a
         * credential cache and build a ap_req.
         */
        retcode = krb5_cc_default(kcontext, &cc);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM: krb5_cc_default failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

        DEBUGMSGTL(("ksm", "KSM: Set credential cache successfully\n"));

        /*
         * This seems odd, since we don't need this until later (or earlier,
         * depending on how you look at it), but because the most likely
         * errors are Kerberos at this point, I'll get this now to save
         * time not encoding the rest of the packet.
         *
         * Also, we need the subkey to encrypt the PDU (if required).
         */

        retcode =
            krb5_mk_req(kcontext, &auth_context,
                        AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
                        (char *) service_name, parms->session->peername, NULL,
                        cc, &outdata);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM: krb5_mk_req failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

	DEBUGMSGTL(("ksm", "KSM: ticket retrieved successfully for \"%s/%s\" "
		    "(may not be actual ticket sname)\n", service_name,
		    parms->session->peername));

    } else {

        /*
         * Grab the auth_context from our security state reference
         */

        auth_context = ksm_state->auth_context;

        /*
         * Bundle up an AP_REP.  Note that we do this only when we
         * have a security state reference (which means we're in an agent
         * and we're sending a response).
         */

        DEBUGMSGTL(("ksm", "KSM: Starting reply processing.\n"));

        retcode = krb5_mk_rep(kcontext, auth_context, &outdata);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM: krb5_mk_rep failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

        DEBUGMSGTL(("ksm", "KSM: Finished with krb5_mk_rep()\n"));
    }

    /*
     * If we have to encrypt the PDU, do that now
     */

    if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {

        DEBUGMSGTL(("ksm", "KSM: Starting PDU encryption.\n"));

        /*
         * It's weird -
         *
         * If we're on the manager, it's a local subkey (because that's in
         * our AP_REQ)
         *
         * If we're on the agent, it's a remote subkey (because that comes
         * FROM the received AP_REQ).
         */

        if (ksm_state)
            retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context,
                                                    &subkey);
        else
            retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context,
                                                   &subkey);

        if (retcode) {
            DEBUGMSGTL(("ksm",
                        "KSM: krb5_auth_con_getlocalsubkey failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

        /*
         * Note that here we need to handle different things between the
         * old and new crypto APIs.  First, we need to get the final encrypted
         * length of the PDU.
         */

#ifdef MIT_NEW_CRYPTO
        retcode = krb5_c_encrypt_length(kcontext, subkey->enctype,
                                        parms->scopedPduLen,
                                        &encrypted_length);

        if (retcode) {
            DEBUGMSGTL(("ksm",
                        "Encryption length calculation failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }
#else                           /* MIT_NEW_CRYPTO */

        krb5_use_enctype(kcontext, &eblock, subkey->enctype);
        retcode = krb5_process_key(kcontext, &eblock, subkey);

        if (retcode) {
            DEBUGMSGTL(("ksm", "krb5_process_key failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

        encrypted_length = krb5_encrypt_size(parms->scopedPduLen,
                                             eblock.crypto_entry);
#endif                          /* MIT_NEW_CRYPTO */

        encrypted_data = malloc(encrypted_length);

        if (!encrypted_data) {
            DEBUGMSGTL(("ksm",
                        "KSM: Unable to malloc %d bytes for encrypt "
                        "buffer: %s\n", parms->scopedPduLen,
                        strerror(errno)));
            retval = SNMPERR_MALLOC;
#ifndef MIT_NEW_CRYPTO
            krb5_finish_key(kcontext, &eblock);
#endif                          /* ! MIT_NEW_CRYPTO */

            goto error;
        }

        /*
         * We need to set up a blank initialization vector for the encryption.
         * Use a block of all zero's (which is dependent on the block size
         * of the encryption method).
         */

#ifdef MIT_NEW_CRYPTO

        retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize);

        if (retcode) {
            DEBUGMSGTL(("ksm",
                        "Unable to determine crypto block size: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }
#else                           /* MIT_NEW_CRYPTO */

        blocksize =
            krb5_enctype_array[subkey->enctype]->system->block_length;

#endif                          /* MIT_NEW_CRYPTO */

        ivector.data = malloc(blocksize);

        if (!ivector.data) {
            DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n",
                        blocksize));
            retval = SNMPERR_MALLOC;
            goto error;
        }

        ivector.length = blocksize;
        memset(ivector.data, 0, blocksize);

        /*
         * Finally!  Do the encryption!
         */

#ifdef MIT_NEW_CRYPTO

        input.data = (char *) parms->scopedPdu;
        input.length = parms->scopedPduLen;
        output.ciphertext.data = (char *) encrypted_data;
        output.ciphertext.length = encrypted_length;

        retcode =
            krb5_c_encrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
                           &ivector, &input, &output);

#else                           /* MIT_NEW_CRYPTO */

        retcode = krb5_encrypt(kcontext, (krb5_pointer) parms->scopedPdu,
                               (krb5_pointer) encrypted_data,
                               parms->scopedPduLen, &eblock, ivector.data);

        krb5_finish_key(kcontext, &eblock);

#endif                          /* MIT_NEW_CRYPTO */

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM: krb5_encrypt failed: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            snmp_set_detail(error_message(retcode));
            goto error;
        }

	*offset = 0;

        rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen,
                                             offset, 1,
                                             (u_char) (ASN_UNIVERSAL |
                                                       ASN_PRIMITIVE |
                                                       ASN_OCTET_STR),
                                             encrypted_data,
                                             encrypted_length);

        if (rc == 0) {
            DEBUGMSGTL(("ksm", "Building encrypted payload failed.\n"));
            retval = SNMPERR_TOO_LONG;
            goto error;
        }

        DEBUGMSGTL(("ksm", "KSM: Encryption complete.\n"));

    } else {
        /*
         * Plaintext PDU (not encrypted)
         */

        if (*parms->wholeMsgLen < parms->scopedPduLen) {
            DEBUGMSGTL(("ksm", "Not enough room for plaintext PDU.\n"));
            retval = SNMPERR_TOO_LONG;
            goto error;
        }
    }

    /*
     * Start encoding the msgSecurityParameters
     *
     * For now, use 0 for the response hint
     */

    DEBUGMSGTL(("ksm", "KSM: scopedPdu added to payload\n"));

    seq_offset = *offset;

    rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen,
                                      offset, 1,
                                      (u_char) (ASN_UNIVERSAL |
                                                ASN_PRIMITIVE |
                                                ASN_INTEGER),
                                      (long *) &zero, sizeof(zero));

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen,
                                         offset, 1,
                                         (u_char) (ASN_UNIVERSAL |
                                                   ASN_PRIMITIVE |
                                                   ASN_OCTET_STR),
                                         (u_char *) outdata.data,
                                         outdata.length);

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building ksm AP_REQ failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    /*
     * Now, we need to pick the "right" checksum algorithm.  For old
     * crypto, just pick CKSUMTYPE_RSA_MD5_DES; for new crypto, pick
     * one of the "approved" ones.
     */

#ifdef MIT_NEW_CRYPTO
    retcode = krb5_c_keyed_checksum_types(kcontext, subkey->enctype,
                                          &numcksumtypes, &cksumtype_array);

    if (retcode) {
	DEBUGMSGTL(("ksm", "Unable to find appropriate keyed checksum: %s\n",
		    error_message(retcode)));
	snmp_set_detail(error_message(retcode));
        retval = SNMPERR_KRB5;
        goto error;
    }

    if (numcksumtypes <= 0) {
	DEBUGMSGTL(("ksm", "We received a list of zero cksumtypes for this "
		    "enctype (%d)\n", subkey->enctype));
	snmp_set_detail("No valid checksum type for this encryption type");
	retval = SNMPERR_KRB5;
	goto error;
    }

    /*
     * It's not clear to me from the API which checksum you're supposed
     * to support, so I'm taking a guess at the first one
     */

    cksumtype = cksumtype_array[0];

    krb5_free_cksumtypes(kcontext, cksumtype_array);

    DEBUGMSGTL(("ksm", "KSM: Choosing checksum type of %d (subkey type "
		"of %d)\n", cksumtype, subkey->enctype));

    retcode = krb5_c_checksum_length(kcontext, cksumtype, &blocksize);

    if (retcode) {
        DEBUGMSGTL(("ksm", "Unable to determine checksum length: %s\n",
                    error_message(retcode)));
        snmp_set_detail(error_message(retcode));
        retval = SNMPERR_KRB5;
        goto error;
    }

    pdu_checksum.length = blocksize;

#else /* MIT_NEW_CRYPTO */
    if (ksm_state)
        cksumtype = ksm_state->cksumtype;
    else
	cksumtype = CKSUMTYPE_RSA_MD5_DES;

    if (!is_keyed_cksum(cksumtype)) {
        DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
                    cksumtype));
        snmp_set_detail("Checksum is not a keyed checksum");
        retval = SNMPERR_KRB5;
        goto error;
    }

    if (!is_coll_proof_cksum(cksumtype)) {
        DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
                    "checksum\n", cksumtype));
        snmp_set_detail("Checksum is not a collision-proof checksum");
        retval = SNMPERR_KRB5;
        goto error;
    }

    pdu_checksum.length = krb5_checksum_size(kcontext, cksumtype);
    pdu_checksum.checksum_type = cksumtype;

#endif /* MIT_NEW_CRYPTO */

    /*
     * Note that here, we're just leaving blank space for the checksum;
     * we remember where that is, and we'll fill it in later.
     */

    *offset += pdu_checksum.length;
    memset(*wholeMsg + *parms->wholeMsgLen - *offset, 0, pdu_checksum.length);

    cksum_pointer = *wholeMsg + *parms->wholeMsgLen - *offset;

    rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen,
                                         parms->wholeMsgOffset, 1,
                                         (u_char) (ASN_UNIVERSAL |
                                                   ASN_PRIMITIVE |
                                                   ASN_OCTET_STR),
                                         pdu_checksum.length);

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen,
                                      parms->wholeMsgOffset, 1,
                                      (u_char) (ASN_UNIVERSAL |
                                                ASN_PRIMITIVE |
                                                ASN_OCTET_STR),
                                      (long *) &cksumtype,
                                      sizeof(cksumtype));

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen,
                                           parms->wholeMsgOffset, 1,
                                           (u_char) (ASN_SEQUENCE |
                                                     ASN_CONSTRUCTOR),
                                           *offset - seq_offset);

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen,
                                         parms->wholeMsgOffset, 1,
                                         (u_char) (ASN_UNIVERSAL |
                                                   ASN_PRIMITIVE |
                                                   ASN_OCTET_STR),
                                         *offset - seq_offset);

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    DEBUGMSGTL(("ksm", "KSM: Security parameter encoding completed\n"));

    /*
     * We're done with the KSM security parameters - now we do the global
     * header and wrap up the whole PDU.
     */

    if (*parms->wholeMsgLen < parms->globalDataLen) {
        DEBUGMSGTL(("ksm", "Building global data failed.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    *offset += parms->globalDataLen;
    memcpy(*wholeMsg + *parms->wholeMsgLen - *offset,
	   parms->globalData, parms->globalDataLen);

    rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen,
                                           offset, 1,
                                           (u_char) (ASN_SEQUENCE |
                                                     ASN_CONSTRUCTOR),
                                           *offset);

    if (rc == 0) {
        DEBUGMSGTL(("ksm", "Building master packet sequence.\n"));
        retval = SNMPERR_TOO_LONG;
        goto error;
    }

    DEBUGMSGTL(("ksm", "KSM: PDU master packet encoding complete.\n"));

    /*
     * Now we need to checksum the entire PDU (since it's built).
     */

    pdu_checksum.contents = malloc(pdu_checksum.length);

    if (!pdu_checksum.contents) {
        DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum\n",
                    pdu_checksum.length));
        retval = SNMPERR_MALLOC;
        goto error;
    }

    /*
     * If we didn't encrypt the packet, we haven't yet got the subkey.
     * Get that now.
     */

    if (!subkey) {
        if (ksm_state)
            retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context,
                                                    &subkey);
        else
            retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context,
                                                   &subkey);
        if (retcode) {
            DEBUGMSGTL(("ksm", "krb5_auth_con_getlocalsubkey failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }
    }
#ifdef MIT_NEW_CRYPTO

    input.data = (char *) (*wholeMsg + *parms->wholeMsgLen - *offset);
    input.length = *offset;
        retcode = krb5_c_make_checksum(kcontext, cksumtype, subkey,
                                       KSM_KEY_USAGE_CHECKSUM, &input,
                                       &pdu_checksum);

#else                           /* MIT_NEW_CRYPTO */

    retcode = krb5_calculate_checksum(kcontext, cksumtype, *wholeMsg +
				      *parms->wholeMsgLen - *offset,
                                      *offset,
                                      (krb5_pointer) subkey->contents,
                                      subkey->length, &pdu_checksum);

#endif                          /* MIT_NEW_CRYPTO */

    if (retcode) {
        DEBUGMSGTL(("ksm", "Calculate checksum failed: %s\n",
                    error_message(retcode)));
        retval = SNMPERR_KRB5;
        snmp_set_detail(error_message(retcode));
        goto error;
    }

    DEBUGMSGTL(("ksm", "KSM: Checksum calculation complete.\n"));

    memcpy(cksum_pointer, pdu_checksum.contents, pdu_checksum.length);

    DEBUGMSGTL(("ksm", "KSM: Writing checksum of %d bytes at offset %d\n",
                pdu_checksum.length, cksum_pointer - (*wholeMsg + 1)));

    DEBUGMSGTL(("ksm", "KSM: Checksum:"));

    for (i = 0; i < pdu_checksum.length; i++)
        DEBUGMSG(("ksm", " %02x",
                  (unsigned int) pdu_checksum.contents[i]));

    DEBUGMSG(("ksm", "\n"));

    /*
     * If we're _not_ called as part of a response (null ksm_state),
     * then save the auth_context for later using our cache routines.
     */

    if (!ksm_state) {
        if ((retval = ksm_insert_cache(parms->pdu->msgid, auth_context,
                                       (u_char *) parms->secName,
                                       parms->secNameLen)) !=
            SNMPERR_SUCCESS)
            goto error;
        auth_context = NULL;
    }

    DEBUGMSGTL(("ksm", "KSM processing complete!\n"));

  error:

    if (pdu_checksum.contents)
#ifdef MIT_NEW_CRYPTO
        krb5_free_checksum_contents(kcontext, &pdu_checksum);
#else                           /* MIT_NEW_CRYPTO */
        free(pdu_checksum.contents);
#endif                          /* MIT_NEW_CRYPTO */

    if (ivector.data)
        free(ivector.data);

    if (subkey)
        krb5_free_keyblock(kcontext, subkey);

    if (encrypted_data)
        free(encrypted_data);

    if (cc)
        krb5_cc_close(kcontext, cc);

    if (auth_context && !ksm_state)
        krb5_auth_con_free(kcontext, auth_context);

    return retval;
}
Beispiel #12
0
/*
 * krb5_auth_context_externalize()	- Externalize the krb5_auth_context.
 */
static krb5_error_code
krb5_auth_context_externalize(krb5_context kcontext, krb5_pointer arg, krb5_octet **buffer, size_t *lenremain)
{
    krb5_error_code	kret;
    krb5_auth_context	auth_context;
    size_t		required;
    krb5_octet		*bp;
    size_t		remain;
    size_t              obuf;
    krb5_int32		obuf32;

    required = 0;
    bp = *buffer;
    remain = *lenremain;
    kret = EINVAL;
    if ((auth_context = (krb5_auth_context) arg)) {
	kret = ENOMEM;
	if (!krb5_auth_context_size(kcontext, arg, &required) &&
	    (required <= remain)) {

	    /* Write fixed portion */
	    (void) krb5_ser_pack_int32(KV5M_AUTH_CONTEXT, &bp, &remain);
	    (void) krb5_ser_pack_int32(auth_context->auth_context_flags,
				       &bp, &remain);
	    (void) krb5_ser_pack_int32(auth_context->remote_seq_number,
				       &bp, &remain);
	    (void) krb5_ser_pack_int32(auth_context->local_seq_number,
				       &bp, &remain);
	    (void) krb5_ser_pack_int32((krb5_int32) auth_context->req_cksumtype,
				       &bp, &remain);
	    (void) krb5_ser_pack_int32((krb5_int32) auth_context->safe_cksumtype,
				       &bp, &remain);

	    kret = 0;

	    /* Now figure out the number of bytes for i_vector and write it */
	    if (auth_context->i_vector) {
		kret = krb5_c_block_size(kcontext,
					 auth_context->keyblock->enctype,
					 &obuf);
	    } else {
		obuf = 0;
	    }

	    /* Convert to signed 32 bit integer */
	    obuf32 = obuf;
	    if (kret == 0 && obuf != obuf32)
		kret = EINVAL;
	    if (!kret)
		(void) krb5_ser_pack_int32(obuf32, &bp, &remain);

	    /* Now copy i_vector */
	    if (!kret && auth_context->i_vector)
		(void) krb5_ser_pack_bytes(auth_context->i_vector,
					   obuf,
					   &bp, &remain);

	    /* Now handle remote_addr, if appropriate */
	    if (!kret && auth_context->remote_addr) {
		(void) krb5_ser_pack_int32(TOKEN_RADDR, &bp, &remain);
		kret = krb5_externalize_opaque(kcontext,
					       KV5M_ADDRESS,
					       (krb5_pointer)
					       auth_context->remote_addr,
					       &bp,
					       &remain);
	    }

	    /* Now handle remote_port, if appropriate */
	    if (!kret && auth_context->remote_port) {
		(void) krb5_ser_pack_int32(TOKEN_RPORT, &bp, &remain);
		kret = krb5_externalize_opaque(kcontext,
					       KV5M_ADDRESS,
					       (krb5_pointer)
					       auth_context->remote_addr,
					       &bp,
					       &remain);
	    }

	    /* Now handle local_addr, if appropriate */
	    if (!kret && auth_context->local_addr) {
		(void) krb5_ser_pack_int32(TOKEN_LADDR, &bp, &remain);
		kret = krb5_externalize_opaque(kcontext,
					       KV5M_ADDRESS,
					       (krb5_pointer)
					       auth_context->local_addr,
					       &bp,
					       &remain);
	    }

	    /* Now handle local_port, if appropriate */
	    if (!kret && auth_context->local_port) {
		(void) krb5_ser_pack_int32(TOKEN_LPORT, &bp, &remain);
		kret = krb5_externalize_opaque(kcontext,
					       KV5M_ADDRESS,
					       (krb5_pointer)
					       auth_context->local_addr,
					       &bp,
					       &remain);
	    }

	    /* Now handle keyblock, if appropriate */
	    if (!kret && auth_context->keyblock) {
		(void) krb5_ser_pack_int32(TOKEN_KEYBLOCK, &bp, &remain);
		kret = krb5_externalize_opaque(kcontext,
					       KV5M_KEYBLOCK,
					       (krb5_pointer)
					       auth_context->keyblock,
					       &bp,
					       &remain);
	    }

	    /* Now handle subkey, if appropriate */
	    if (!kret && auth_context->send_subkey) {
		(void) krb5_ser_pack_int32(TOKEN_LSKBLOCK, &bp, &remain);
		kret = krb5_externalize_opaque(kcontext,
					       KV5M_KEYBLOCK,
					       (krb5_pointer)
					       auth_context->send_subkey,
					       &bp,
					       &remain);
	    }

	    /* Now handle subkey, if appropriate */
	    if (!kret && auth_context->recv_subkey) {
		(void) krb5_ser_pack_int32(TOKEN_RSKBLOCK, &bp, &remain);
		kret = krb5_externalize_opaque(kcontext,
					       KV5M_KEYBLOCK,
					       (krb5_pointer)
					       auth_context->recv_subkey,
					       &bp,
					       &remain);
	    }

	    /* Now handle authentp, if appropriate */
	    if (!kret && auth_context->authentp)
		kret = krb5_externalize_opaque(kcontext,
					       KV5M_AUTHENTICATOR,
					       (krb5_pointer)
					       auth_context->authentp,
					       &bp,
					       &remain);

	    /*
	     * If we were successful, write trailer then update the pointer and
	     * remaining length;
	     */
	    if (!kret) {
		/* Write our trailer */
		(void) krb5_ser_pack_int32(KV5M_AUTH_CONTEXT, &bp, &remain);
		*buffer = bp;
		*lenremain = remain;
	    }
	}
    }
    return(kret);
}
int Condor_Auth_Kerberos :: unwrap(char*  input, 
                                   int    /* input_len */, 
                                   char*& output, 
                                   int& output_len)
{
    krb5_error_code code;
    krb5_data       out_data;
    krb5_enc_data   enc_data;
    int index = 0, tmp;
	size_t blocksize;
    out_data.data = 0;
    out_data.length = 0;

    memcpy(&tmp, input, sizeof(enc_data.enctype));
    enc_data.enctype = ntohl(tmp);
    index += sizeof(enc_data.enctype);

    memcpy(&tmp, input + index, sizeof(enc_data.kvno));
    enc_data.kvno = ntohl(tmp);
    index += sizeof(enc_data.kvno);

    memcpy(&tmp, input + index, sizeof(enc_data.ciphertext.length));
    enc_data.ciphertext.length = ntohl(tmp);
    index += sizeof(enc_data.ciphertext.length);

    enc_data.ciphertext.data = input + index;

	// DEBUG
	dprintf (D_FULLDEBUG, "KERBEROS: input.enctype (%i) and session.enctype (%i)\n",
			enc_data.enctype, sessionKey_->enctype);

	// make a blank initialization vector
	code = krb5_c_block_size(krb_context_, sessionKey_->enctype, &blocksize);
	if (code) {
    	dprintf(D_ALWAYS, "AUTH_ERROR: %s\n", error_message(code));
	}

    out_data.length = enc_data.ciphertext.length;
	out_data.data = (char*)malloc(out_data.length);

	if ((code = krb5_c_decrypt(krb_context_, sessionKey_, 1024, /* key usage */
				0, &enc_data, &out_data))!=0) {			/* 0 = no ivec */

	//if (code = krb5_decrypt_data(krb_context_, sessionKey_, 0, &enc_data, &out_data)) {
        output_len = 0;
        output = 0;
        dprintf( D_ALWAYS, "KERBEROS: %s\n", error_message(code) );
        if (out_data.data) {
            free(out_data.data);
        }
        return false;
    }
    
    output_len = out_data.length;
    output = (char *) malloc(output_len);
    memcpy(output, out_data.data, output_len);

    if (out_data.data) {
        free(out_data.data);
    }
    return true;
}
int Condor_Auth_Kerberos :: wrap(char*  input, 
                                 int    input_len, 
                                 char*& output, 
                                 int&   output_len)
{
    krb5_error_code code;
    krb5_data       in_data;
    krb5_enc_data   out_data;
    int             index, tmp;

	size_t blocksize, encrypted_length;
	char* encrypted_data = 0;

	// make a blank initialization vector
	code = krb5_c_block_size(krb_context_, sessionKey_->enctype, &blocksize);
	if (code) {
		// err
	}

    // Make the input buffer
    in_data.data = input;
    in_data.length = input_len;

    // Make the output buffer
    code = krb5_c_encrypt_length(krb_context_, sessionKey_->enctype, input_len, &encrypted_length);
	if(code) {
		// err
	}

	encrypted_data = (char*)malloc(encrypted_length);
	if (!encrypted_length) {
		// err
	}

    out_data.ciphertext.data = (char*)encrypted_data;
	out_data.ciphertext.length = encrypted_length;

    if ((code = krb5_c_encrypt(krb_context_, sessionKey_, 1024, /* key usage */
				0, &in_data, &out_data)) != 0) {			/* 0 = no ivec */
        output     = 0;
        output_len = 0;
        if (out_data.ciphertext.data) {    
            free(out_data.ciphertext.data);
        }
        dprintf( D_ALWAYS, "KERBEROS: %s\n", error_message(code) );
        return false;
    }
    
    output_len = sizeof(out_data.enctype) +
        sizeof(out_data.kvno)    + 
        sizeof(out_data.ciphertext.length) +
        out_data.ciphertext.length;
    
    output = (char *) malloc(output_len);
    index = 0;
    tmp = htonl(out_data.enctype);
    memcpy(output + index, &tmp, sizeof(out_data.enctype));
    index += sizeof(out_data.enctype);

    tmp = htonl(out_data.kvno);
    memcpy(output + index, &tmp, sizeof(out_data.kvno));
    index += sizeof(out_data.kvno);

    tmp = htonl(out_data.ciphertext.length);
    memcpy(output + index, &tmp, sizeof(out_data.ciphertext.length));
    index += sizeof(out_data.ciphertext.length);

    if (out_data.ciphertext.data) {    
	memcpy(output + index, out_data.ciphertext.data,
		out_data.ciphertext.length);
        free(out_data.ciphertext.data);
    }

    return TRUE;
}
Beispiel #15
0
OM_uint32
kg_seal_iov_length(OM_uint32 *minor_status,
                   gss_ctx_id_t context_handle,
                   int conf_req_flag,
                   gss_qop_t qop_req,
                   int *conf_state,
                   gss_iov_buffer_desc *iov,
                   int iov_count)
{
    krb5_gss_ctx_id_rec *ctx;
    gss_iov_buffer_t header, trailer, padding;
    size_t data_length, assoc_data_length;
    size_t gss_headerlen, gss_padlen, gss_trailerlen;
    unsigned int k5_headerlen = 0, k5_trailerlen = 0, k5_padlen = 0;
    krb5_error_code code;
    krb5_context context;
    int dce_style;

    if (qop_req != GSS_C_QOP_DEFAULT) {
        *minor_status = (OM_uint32)G_UNKNOWN_QOP;
        return GSS_S_FAILURE;
    }

    if (!kg_validate_ctx_id(context_handle)) {
        *minor_status = (OM_uint32)G_VALIDATE_FAILED;
        return GSS_S_NO_CONTEXT;
    }

    ctx = (krb5_gss_ctx_id_rec *)context_handle;
    if (!ctx->established) {
        *minor_status = KG_CTX_INCOMPLETE;
        return GSS_S_NO_CONTEXT;
    }

    header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
    if (header == NULL) {
        *minor_status = EINVAL;
        return GSS_S_FAILURE;
    }
    INIT_IOV_DATA(header);

    trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
    if (trailer != NULL) {
        INIT_IOV_DATA(trailer);
    }

    dce_style = ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0);

    /* For CFX, EC is used instead of padding, and is placed in header or trailer */
    padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
    if (padding == NULL) {
        if (conf_req_flag && ctx->proto == 0 && !dce_style) {
            *minor_status = EINVAL;
            return GSS_S_FAILURE;
        }
    } else {
        INIT_IOV_DATA(padding);
    }

    kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);

    if (conf_req_flag && kg_integ_only_iov(iov, iov_count))
        conf_req_flag = FALSE;

    context = ctx->k5_context;

    gss_headerlen = gss_padlen = gss_trailerlen = 0;

    if (ctx->proto == 1) {
        krb5_enctype enctype;
        size_t ec;

        if (ctx->have_acceptor_subkey)
            enctype = ctx->acceptor_subkey->enctype;
        else
            enctype = ctx->subkey->enctype;

        code = krb5_c_crypto_length(context, enctype,
                                    conf_req_flag ?
                                        KRB5_CRYPTO_TYPE_TRAILER : KRB5_CRYPTO_TYPE_CHECKSUM,
                                    &k5_trailerlen);
        if (code != 0) {
            *minor_status = code;
            return GSS_S_FAILURE;
        }

        if (conf_req_flag) {
            code = krb5_c_crypto_length(context, enctype, KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen);
            if (code != 0) {
                *minor_status = code;
                return GSS_S_FAILURE;
            }
        }

        gss_headerlen = 16; /* Header */
        if (conf_req_flag) {
            gss_headerlen += k5_headerlen; /* Kerb-Header */
            gss_trailerlen = 16 /* E(Header) */ + k5_trailerlen; /* Kerb-Trailer */

            code = krb5_c_padding_length(context, enctype,
                                         data_length - assoc_data_length + 16 /* E(Header) */, &k5_padlen);
            if (code != 0) {
                *minor_status = code;
                return GSS_S_FAILURE;
            }

            if (k5_padlen == 0 && dce_style) {
                /* Windows rejects AEAD tokens with non-zero EC */
                code = krb5_c_block_size(context, enctype, &ec);
                if (code != 0) {
                    *minor_status = code;
                    return GSS_S_FAILURE;
                }
            } else
                ec = k5_padlen;

            gss_trailerlen += ec;
        } else {
            gss_trailerlen = k5_trailerlen; /* Kerb-Checksum */
        }
    } else if (!dce_style) {
        k5_padlen = (ctx->sealalg == SEAL_ALG_MICROSOFT_RC4) ? 1 : 8;

        if (k5_padlen == 1)
            gss_padlen = 1;
        else
            gss_padlen = k5_padlen - ((data_length - assoc_data_length) % k5_padlen);
    }

    data_length += gss_padlen;

    if (ctx->proto == 0) {
        /* Header | Checksum | Confounder | Data | Pad */
        size_t data_size;

        k5_headerlen = kg_confounder_size(context, ctx->enc);

        data_size = 14 /* Header */ + ctx->cksum_size + k5_headerlen;

        if (!dce_style)
            data_size += data_length;

        gss_headerlen = g_token_size(ctx->mech_used, data_size);

        /* g_token_size() will include data_size as well as the overhead, so
         * subtract data_length just to get the overhead (ie. token size) */
        if (!dce_style)
            gss_headerlen -= data_length;
    }

    if (minor_status != NULL)
        *minor_status = 0;

    if (trailer == NULL)
        gss_headerlen += gss_trailerlen;
    else
        trailer->buffer.length = gss_trailerlen;

    assert(gss_padlen == 0 || padding != NULL);

    if (padding != NULL)
        padding->buffer.length = gss_padlen;

    header->buffer.length = gss_headerlen;

    if (conf_state != NULL)
        *conf_state = conf_req_flag;

    return GSS_S_COMPLETE;
}
Beispiel #16
0
static krb5_error_code
krb5_rd_priv_basic(krb5_context context, const krb5_data *inbuf,
		   const krb5_keyblock *keyblock,
		   const krb5_address *local_addr,
		   const krb5_address *remote_addr, krb5_pointer i_vector,
		   krb5_replay_data *replaydata, krb5_data *outbuf)
{
    krb5_error_code 	  retval;
    krb5_priv 		* privmsg;
    krb5_data 		  scratch;
    krb5_priv_enc_part  * privmsg_enc_part;
    size_t		  blocksize;
    krb5_data		  ivdata;

    if (!krb5_is_krb_priv(inbuf))
	return KRB5KRB_AP_ERR_MSG_TYPE;

    /* decode private message */
    if ((retval = decode_krb5_priv(inbuf, &privmsg)))
	return retval;
    
    if (i_vector) {
	if ((retval = krb5_c_block_size(context, keyblock->enctype,
					&blocksize)))
	    goto cleanup_privmsg;

	ivdata.length = blocksize;
	ivdata.data = i_vector;
    }

    scratch.length = privmsg->enc_part.ciphertext.length;
    if (!(scratch.data = malloc(scratch.length))) {
	retval = ENOMEM;
	goto cleanup_privmsg;
    }

    if ((retval = krb5_c_decrypt(context, keyblock,
				 KRB5_KEYUSAGE_KRB_PRIV_ENCPART, 
				 i_vector?&ivdata:0,
				 &privmsg->enc_part, &scratch)))
	goto cleanup_scratch;

    /*  now decode the decrypted stuff */
    if ((retval = decode_krb5_enc_priv_part(&scratch, &privmsg_enc_part)))
        goto cleanup_scratch;

    if (!krb5_address_compare(context,remote_addr,privmsg_enc_part->s_address)){
	retval = KRB5KRB_AP_ERR_BADADDR;
	goto cleanup_data;
    }
    
    if (privmsg_enc_part->r_address) {
	if (local_addr) {
	    if (!krb5_address_compare(context, local_addr,
				      privmsg_enc_part->r_address)) {
		retval = KRB5KRB_AP_ERR_BADADDR;
		goto cleanup_data;
	    }
	} else {
	    krb5_address **our_addrs;
	
	    if ((retval = krb5_os_localaddr(context, &our_addrs))) {
		goto cleanup_data;
	    }
	    if (!krb5_address_search(context, privmsg_enc_part->r_address, 
				     our_addrs)) {
		krb5_free_addresses(context, our_addrs);
		retval =  KRB5KRB_AP_ERR_BADADDR;
		goto cleanup_data;
	    }
	    krb5_free_addresses(context, our_addrs);
	}
    }

    replaydata->timestamp = privmsg_enc_part->timestamp;
    replaydata->usec = privmsg_enc_part->usec;
    replaydata->seq = privmsg_enc_part->seq_number;

    /* everything is ok - return data to the user */
    *outbuf = privmsg_enc_part->user_data;
    retval = 0;

cleanup_data:;
    if (retval == 0)
	privmsg_enc_part->user_data.data = 0;
    krb5_free_priv_enc_part(context, privmsg_enc_part);

cleanup_scratch:;
    memset(scratch.data, 0, scratch.length); 
    free(scratch.data);

cleanup_privmsg:;
    free(privmsg->enc_part.ciphertext.data); 
    free(privmsg);

    return retval;
}
krb5_error_code
gss_krb5int_make_seal_token_v3_iov(krb5_context context,
                                   krb5_gss_ctx_id_rec *ctx,
                                   int conf_req_flag,
                                   int *conf_state,
                                   gss_iov_buffer_desc *iov,
                                   int iov_count,
                                   int toktype)
{
    krb5_error_code code = 0;
    gss_iov_buffer_t header;
    gss_iov_buffer_t padding;
    gss_iov_buffer_t trailer;
    unsigned char acceptor_flag;
    unsigned short tok_id;
    unsigned char *outbuf = NULL;
    unsigned char *tbuf = NULL;
    int key_usage;
    size_t rrc = 0;
    unsigned int  gss_headerlen, gss_trailerlen;
    krb5_key key;
    krb5_cksumtype cksumtype;
    size_t data_length, assoc_data_length;

    assert(ctx->big_endian == 0);
    assert(ctx->proto == 1);

    acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
    key_usage = (toktype == KG_TOK_WRAP_MSG
                 ? (ctx->initiate
                    ? KG_USAGE_INITIATOR_SEAL
                    : KG_USAGE_ACCEPTOR_SEAL)
                 : (ctx->initiate
                    ? KG_USAGE_INITIATOR_SIGN
                    : KG_USAGE_ACCEPTOR_SIGN));
    if (ctx->have_acceptor_subkey) {
        key = ctx->acceptor_subkey;
        cksumtype = ctx->acceptor_subkey_cksumtype;
    } else {
        key = ctx->subkey;
        cksumtype = ctx->cksumtype;
    }
    assert(key != NULL);
    assert(cksumtype != 0);

    kg_iov_msglen(iov, iov_count, &data_length, &assoc_data_length);

    header = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
    if (header == NULL)
        return EINVAL;

    padding = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
    if (padding != NULL)
        padding->buffer.length = 0;

    trailer = kg_locate_iov(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);

    if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
        unsigned int k5_headerlen, k5_trailerlen, k5_padlen;
        size_t ec = 0;
        size_t conf_data_length = data_length - assoc_data_length;

        code = krb5_c_crypto_length(context, key->keyblock.enctype,
                                    KRB5_CRYPTO_TYPE_HEADER, &k5_headerlen);
        if (code != 0)
            goto cleanup;

        code = krb5_c_padding_length(context, key->keyblock.enctype,
                                     conf_data_length + 16 /* E(Header) */, &k5_padlen);
        if (code != 0)
            goto cleanup;

        if (k5_padlen == 0 && (ctx->gss_flags & GSS_C_DCE_STYLE)) {
            /* Windows rejects AEAD tokens with non-zero EC */
            code = krb5_c_block_size(context, key->keyblock.enctype, &ec);
            if (code != 0)
                goto cleanup;
        } else
            ec = k5_padlen;

        code = krb5_c_crypto_length(context, key->keyblock.enctype,
                                    KRB5_CRYPTO_TYPE_TRAILER, &k5_trailerlen);
        if (code != 0)
            goto cleanup;

        gss_headerlen = 16 /* Header */ + k5_headerlen;
        gss_trailerlen = ec + 16 /* E(Header) */ + k5_trailerlen;

        if (trailer == NULL) {
            rrc = gss_trailerlen;
            /* Workaround for Windows bug where it rotates by EC + RRC */
            if (ctx->gss_flags & GSS_C_DCE_STYLE)
                rrc -= ec;
            gss_headerlen += gss_trailerlen;
        }

        if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
            code = kg_allocate_iov(header, (size_t) gss_headerlen);
        } else if (header->buffer.length < gss_headerlen)
            code = KRB5_BAD_MSIZE;
        if (code != 0)
            goto cleanup;
        outbuf = (unsigned char *)header->buffer.value;
        header->buffer.length = (size_t) gss_headerlen;

        if (trailer != NULL) {
            if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
                code = kg_allocate_iov(trailer, (size_t) gss_trailerlen);
            else if (trailer->buffer.length < gss_trailerlen)
                code = KRB5_BAD_MSIZE;
            if (code != 0)
                goto cleanup;
            trailer->buffer.length = (size_t) gss_trailerlen;
        }

        /* TOK_ID */
        store_16_be(KG2_TOK_WRAP_MSG, outbuf);
        /* flags */
        outbuf[2] = (acceptor_flag
                     | (conf_req_flag ? FLAG_WRAP_CONFIDENTIAL : 0)
                     | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
        /* filler */
        outbuf[3] = 0xFF;
        /* EC */
        store_16_be(ec, outbuf + 4);
        /* RRC */
        store_16_be(0, outbuf + 6);
        store_64_be(ctx->seq_send, outbuf + 8);

        /* EC | copy of header to be encrypted, located in (possibly rotated) trailer */
        if (trailer == NULL)
            tbuf = (unsigned char *)header->buffer.value + 16; /* Header */
        else
            tbuf = (unsigned char *)trailer->buffer.value;

        memset(tbuf, 0xFF, ec);
        memcpy(tbuf + ec, header->buffer.value, 16);

        code = kg_encrypt_iov(context, ctx->proto,
                              ((ctx->gss_flags & GSS_C_DCE_STYLE) != 0),
                              ec, rrc, key, key_usage, 0, iov, iov_count);
        if (code != 0)
            goto cleanup;

        /* RRC */
        store_16_be(rrc, outbuf + 6);

        ctx->seq_send++;
    } else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
        tok_id = KG2_TOK_WRAP_MSG;

wrap_with_checksum:

        gss_headerlen = 16;

        code = krb5_c_crypto_length(context, key->keyblock.enctype,
                                    KRB5_CRYPTO_TYPE_CHECKSUM,
                                    &gss_trailerlen);
        if (code != 0)
            goto cleanup;

        assert(gss_trailerlen <= 0xFFFF);

        if (trailer == NULL) {
            rrc = gss_trailerlen;
            gss_headerlen += gss_trailerlen;
        }

        if (header->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
            code = kg_allocate_iov(header, (size_t) gss_headerlen);
        else if (header->buffer.length < gss_headerlen)
            code = KRB5_BAD_MSIZE;
        if (code != 0)
            goto cleanup;
        outbuf = (unsigned char *)header->buffer.value;
        header->buffer.length = (size_t) gss_headerlen;

        if (trailer != NULL) {
            if (trailer->type & GSS_IOV_BUFFER_FLAG_ALLOCATE)
                code = kg_allocate_iov(trailer, (size_t) gss_trailerlen);
            else if (trailer->buffer.length < gss_trailerlen)
                code = KRB5_BAD_MSIZE;
            if (code != 0)
                goto cleanup;
            trailer->buffer.length = (size_t) gss_trailerlen;
        }

        /* TOK_ID */
        store_16_be(tok_id, outbuf);
        /* flags */
        outbuf[2] = (acceptor_flag
                     | (ctx->have_acceptor_subkey ? FLAG_ACCEPTOR_SUBKEY : 0));
        /* filler */
        outbuf[3] = 0xFF;
        if (toktype == KG_TOK_WRAP_MSG) {
            /* Use 0 for checksum calculation, substitute
             * checksum length later.
             */
            /* EC */
            store_16_be(0, outbuf + 4);
            /* RRC */
            store_16_be(0, outbuf + 6);
        } else {
            /* MIC and DEL store 0xFF in EC and RRC */
            store_16_be(0xFFFF, outbuf + 4);
            store_16_be(0xFFFF, outbuf + 6);
        }
        store_64_be(ctx->seq_send, outbuf + 8);

        code = kg_make_checksum_iov_v3(context, cksumtype,
                                       rrc, key, key_usage,
                                       iov, iov_count);
        if (code != 0)
            goto cleanup;

        ctx->seq_send++;

        if (toktype == KG_TOK_WRAP_MSG) {
            /* Fix up EC field */
            store_16_be(gss_trailerlen, outbuf + 4);
            /* Fix up RRC field */
            store_16_be(rrc, outbuf + 6);
        }
    } else if (toktype == KG_TOK_MIC_MSG) {
        tok_id = KG2_TOK_MIC_MSG;
        trailer = NULL;
        goto wrap_with_checksum;
    } else if (toktype == KG_TOK_DEL_CTX) {
        tok_id = KG2_TOK_DEL_CTX;
        goto wrap_with_checksum;
    } else {
        abort();
    }

    code = 0;
    if (conf_state != NULL)
        *conf_state = conf_req_flag;

cleanup:
    if (code != 0)
        kg_release_iov(iov, iov_count);

    return code;
}
Beispiel #18
0
/*
 * krb5_auth_context_size()	- Determine the size required to externalize
 *				  the krb5_auth_context.
 */
static krb5_error_code
krb5_auth_context_size(krb5_context kcontext, krb5_pointer arg, size_t *sizep)
{
    krb5_error_code	kret;
    krb5_auth_context	auth_context;
    size_t		required;

    /*
     * krb5_auth_context requires at minimum:
     *	krb5_int32		for KV5M_AUTH_CONTEXT
     *	krb5_int32		for auth_context_flags
     *	krb5_int32		for remote_seq_number
     *	krb5_int32		for local_seq_number
     *	krb5_int32		for req_cksumtype
     *	krb5_int32		for safe_cksumtype
     *	krb5_int32		for size of i_vector
     *	krb5_int32		for KV5M_AUTH_CONTEXT
     */
    kret = EINVAL;
    if ((auth_context = (krb5_auth_context) arg)) {
	kret = 0;

	/* Calculate size required by i_vector - ptooey */
	if (auth_context->i_vector && auth_context->keyblock) {
	    kret = krb5_c_block_size(kcontext, auth_context->keyblock->enctype,
				     &required);
	} else {
	    required = 0;
	}

	required += sizeof(krb5_int32)*8;

	/* Calculate size required by remote_addr, if appropriate */
	if (!kret && auth_context->remote_addr) {
	    kret = krb5_size_opaque(kcontext,
				    KV5M_ADDRESS,
				    (krb5_pointer) auth_context->remote_addr,
				    &required);
	    if (!kret)
		required += sizeof(krb5_int32);
	}

	/* Calculate size required by remote_port, if appropriate */
	if (!kret && auth_context->remote_port) {
	    kret = krb5_size_opaque(kcontext,
				    KV5M_ADDRESS,
				    (krb5_pointer) auth_context->remote_port,
				    &required);
	    if (!kret)
		required += sizeof(krb5_int32);
	}

	/* Calculate size required by local_addr, if appropriate */
	if (!kret && auth_context->local_addr) {
	    kret = krb5_size_opaque(kcontext,
				    KV5M_ADDRESS,
				    (krb5_pointer) auth_context->local_addr,
				    &required);
	    if (!kret)
		required += sizeof(krb5_int32);
	}

	/* Calculate size required by local_port, if appropriate */
	if (!kret && auth_context->local_port) {
	    kret = krb5_size_opaque(kcontext,
				    KV5M_ADDRESS,
				    (krb5_pointer) auth_context->local_port,
				    &required);
	    if (!kret)
		required += sizeof(krb5_int32);
	}

	/* Calculate size required by keyblock, if appropriate */
	if (!kret && auth_context->keyblock) {
	    kret = krb5_size_opaque(kcontext,
				    KV5M_KEYBLOCK,
				    (krb5_pointer) auth_context->keyblock,
				    &required);
	    if (!kret)
		required += sizeof(krb5_int32);
	}

	/* Calculate size required by send_subkey, if appropriate */
	if (!kret && auth_context->send_subkey) {
	    kret = krb5_size_opaque(kcontext,
				    KV5M_KEYBLOCK,
				    (krb5_pointer) auth_context->send_subkey,
				    &required);
	    if (!kret)
		required += sizeof(krb5_int32);
	}

	/* Calculate size required by recv_subkey, if appropriate */
	if (!kret && auth_context->recv_subkey) {
	    kret = krb5_size_opaque(kcontext,
				    KV5M_KEYBLOCK,
				    (krb5_pointer) auth_context->recv_subkey,
				    &required);
	    if (!kret)
		required += sizeof(krb5_int32);
	}

	/* Calculate size required by authentp, if appropriate */
	if (!kret && auth_context->authentp)
	    kret = krb5_size_opaque(kcontext,
				    KV5M_AUTHENTICATOR,
				    (krb5_pointer) auth_context->authentp,
				    &required);

    }
    if (!kret)
	*sizep += required;
    return(kret);
}
Beispiel #19
0
int
ksm_process_in_msg(struct snmp_secmod_incoming_params *parms)
{
    long            temp;
    krb5_cksumtype  cksumtype;
    krb5_auth_context auth_context = NULL;
    krb5_error_code retcode;
    krb5_checksum   checksum;
    krb5_data       ap_req, ivector;
    krb5_flags      flags;
    krb5_keyblock  *subkey = NULL;
#ifdef MIT_NEW_CRYPTO
    krb5_data       input, output;
    krb5_boolean    valid;
    krb5_enc_data   in_crypt;
#else                           /* MIT_NEW_CRYPTO */
    krb5_encrypt_block eblock;
#endif                          /* MIT_NEW_CRYPTO */
    krb5_ticket    *ticket = NULL;
    int             retval = SNMPERR_SUCCESS, response = 0;
    size_t          length =
        parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg);
    u_char         *current = parms->secParams, type;
    size_t          cksumlength, blocksize;
    long            hint;
    char           *cname;
    struct ksm_secStateRef *ksm_state;
    struct ksm_cache_entry *entry;

    DEBUGMSGTL(("ksm", "Processing has begun\n"));

    checksum.contents = NULL;
    ap_req.data = NULL;
    ivector.length = 0;
    ivector.data = NULL;

    /*
     * First, parse the security parameters (because we need the subkey inside
     * of the ticket to do anything
     */

    if ((current = asn_parse_sequence(current, &length, &type,
                                      (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                       ASN_OCTET_STR),
                                      "ksm first octet")) == NULL) {
        DEBUGMSGTL(("ksm", "Initial security paramter parsing failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    if ((current = asn_parse_sequence(current, &length, &type,
                                      (ASN_SEQUENCE | ASN_CONSTRUCTOR),
                                      "ksm sequence")) == NULL) {
        DEBUGMSGTL(("ksm",
                    "Security parameter sequence parsing failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    if ((current = asn_parse_int(current, &length, &type, &temp,
                                 sizeof(temp))) == NULL) {
        DEBUGMSGTL(("ksm", "Security parameter checksum type parsing"
                    "failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    cksumtype = temp;

#ifdef MIT_NEW_CRYPTO
    if (!krb5_c_valid_cksumtype(cksumtype)) {
        DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype));

        retval = SNMPERR_KRB5;
        snmp_set_detail("Invalid checksum type");
        goto error;
    }

    if (!krb5_c_is_keyed_cksum(cksumtype)) {
        DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
                    cksumtype));
        snmp_set_detail("Checksum is not a keyed checksum");
        retval = SNMPERR_KRB5;
        goto error;
    }

    if (!krb5_c_is_coll_proof_cksum(cksumtype)) {
        DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
                    "checksum\n", cksumtype));
        snmp_set_detail("Checksum is not a collision-proof checksum");
        retval = SNMPERR_KRB5;
        goto error;
    }
#else /* ! MIT_NEW_CRYPTO */
    if (!valid_cksumtype(cksumtype)) {
        DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype));

        retval = SNMPERR_KRB5;
        snmp_set_detail("Invalid checksum type");
        goto error;
    }

    if (!is_keyed_cksum(cksumtype)) {
        DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
                    cksumtype));
        snmp_set_detail("Checksum is not a keyed checksum");
        retval = SNMPERR_KRB5;
        goto error;
    }

    if (!is_coll_proof_cksum(cksumtype)) {
        DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
                    "checksum\n", cksumtype));
        snmp_set_detail("Checksum is not a collision-proof checksum");
        retval = SNMPERR_KRB5;
        goto error;
    }
#endif /* MIT_NEW_CRYPTO */

    checksum.checksum_type = cksumtype;

    cksumlength = length;

    if ((current = asn_parse_sequence(current, &cksumlength, &type,
                                      (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                       ASN_OCTET_STR), "ksm checksum")) ==
        NULL) {
        DEBUGMSGTL(("ksm",
                    "Security parameter checksum parsing failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    checksum.contents = malloc(cksumlength);
    if (!checksum.contents) {
        DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum.\n",
                    cksumlength));
        retval = SNMPERR_MALLOC;
        goto error;
    }

    memcpy(checksum.contents, current, cksumlength);

    checksum.length = cksumlength;
    checksum.checksum_type = cksumtype;

    /*
     * Zero out the checksum so the validation works correctly
     */

    memset(current, 0, cksumlength);

    current += cksumlength;
    length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);

    if ((current = asn_parse_sequence(current, &length, &type,
                                      (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                       ASN_OCTET_STR), "ksm ap_req")) ==
        NULL) {
        DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing "
                    "failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    ap_req.length = length;
    ap_req.data = malloc(length);
    if (!ap_req.data) {
        DEBUGMSGTL(("ksm",
                    "KSM unable to malloc %d bytes for AP_REQ/REP.\n",
                    length));
        retval = SNMPERR_MALLOC;
        goto error;
    }

    memcpy(ap_req.data, current, length);

    current += length;
    length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);

    if ((current = asn_parse_int(current, &length, &type, &hint,
                                 sizeof(hint))) == NULL) {
        DEBUGMSGTL(("ksm",
                    "KSM security parameter hint parsing failed\n"));

        retval = SNMPERR_ASN_PARSE_ERR;
        goto error;
    }

    /*
     * Okay!  We've got it all!  Now try decoding the damn ticket.
     *
     * But of course there's a WRINKLE!  We need to figure out if we're
     * processing a AP_REQ or an AP_REP.  How do we do that?  We're going
     * to cheat, and look at the first couple of bytes (which is what
     * the Kerberos library routines do anyway).
     *
     * If there are ever new Kerberos message formats, we'll need to fix
     * this here.
     *
     * If it's a _response_, then we need to get the auth_context
     * from our cache.
     */

    if (ap_req.length
        && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) {

        /*
         * We need to initalize the authorization context, and set the
         * replay cache in it (and initialize the replay cache if we
         * haven't already
         */

        retcode = krb5_auth_con_init(kcontext, &auth_context);

        if (retcode) {
            DEBUGMSGTL(("ksm", "krb5_auth_con_init failed: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            snmp_set_detail(error_message(retcode));
            goto error;
        }

        if (!rcache) {
            krb5_data       server;
            server.data = "host";
            server.length = strlen(server.data);

            retcode = krb5_get_server_rcache(kcontext, &server, &rcache);

            if (retcode) {
                DEBUGMSGTL(("ksm", "krb5_get_server_rcache failed: %s\n",
                            error_message(retcode)));
                retval = SNMPERR_KRB5;
                snmp_set_detail(error_message(retcode));
                goto error;
            }
        }

        retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache);

        if (retcode) {
            DEBUGMSGTL(("ksm", "krb5_auth_con_setrcache failed: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            snmp_set_detail(error_message(retcode));
            goto error;
        }

        retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL,
                              keytab, &flags, &ticket);

        krb5_auth_con_setrcache(kcontext, auth_context, NULL);

        if (retcode) {
            DEBUGMSGTL(("ksm", "krb5_rd_req() failed: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            snmp_set_detail(error_message(retcode));
            goto error;
        }

        retcode =
            krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname);

        if (retcode == 0) {
            DEBUGMSGTL(("ksm", "KSM authenticated principal name: %s\n",
                        cname));
            free(cname);
        }

        /*
         * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set
         */

        if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) {
            DEBUGMSGTL(("ksm",
                        "KSM MUTUAL_REQUIRED not set in request!\n"));
            retval = SNMPERR_KRB5;
            snmp_set_detail("MUTUAL_REQUIRED not set in message");
            goto error;
        }

        retcode =
            krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM remote subkey retrieval failed: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            snmp_set_detail(error_message(retcode));
            goto error;
        }

    } else if (ap_req.length && (ap_req.data[0] == 0x6f ||
                                 ap_req.data[0] == 0x4f)) {
        /*
         * Looks like a response; let's see if we've got that auth_context
         * in our cache.
         */

        krb5_ap_rep_enc_part *repl = NULL;

        response = 1;

        entry = ksm_get_cache(parms->pdu->msgid);

        if (!entry) {
            DEBUGMSGTL(("ksm",
                        "KSM: Unable to find auth_context for PDU with "
                        "message ID of %ld\n", parms->pdu->msgid));
            retval = SNMPERR_KRB5;
            goto error;
        }

        auth_context = entry->auth_context;

        /*
         * In that case, let's call the rd_rep function
         */

        retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl);

        if (repl)
            krb5_free_ap_rep_enc_part(kcontext, repl);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() failed: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            goto error;
        }

        DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.\n"));

        retcode =
            krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey);

        if (retcode) {
            DEBUGMSGTL(("ksm", "Unable to retrieve local subkey: %s\n",
                        error_message(retcode)));
            retval = SNMPERR_KRB5;
            snmp_set_detail("Unable to retrieve local subkey");
            goto error;
        }

    } else {
        DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n",
                    ap_req.data[0]));
        retval = SNMPERR_KRB5;
        snmp_set_detail("Unknown Kerberos message type");
        goto error;
    }

#ifdef MIT_NEW_CRYPTO
    input.data = (char *) parms->wholeMsg;
    input.length = parms->wholeMsgLen;

    retcode =
        krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM,
                               &input, &checksum, &valid);
#else                           /* MIT_NEW_CRYPTO */
    retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum,
                                   parms->wholeMsg, parms->wholeMsgLen,
                                   (krb5_pointer) subkey->contents,
                                   subkey->length);
#endif                          /* MIT_NEW_CRYPTO */

    if (retcode) {
        DEBUGMSGTL(("ksm", "KSM checksum verification failed: %s\n",
                    error_message(retcode)));
        retval = SNMPERR_KRB5;
        snmp_set_detail(error_message(retcode));
        goto error;
    }

    /*
     * Don't ask me why they didn't simply return an error, but we have
     * to check to see if "valid" is false.
     */

#ifdef MIT_NEW_CRYPTO
    if (!valid) {
        DEBUGMSGTL(("ksm", "Computed checksum did not match supplied "
                    "checksum!\n"));
        retval = SNMPERR_KRB5;
        snmp_set_detail
            ("Computed checksum did not match supplied checksum");
        goto error;
    }
#endif                          /* MIT_NEW_CRYPTO */

    /*
     * Handle an encrypted PDU.  Note that it's an OCTET_STRING of the
     * output of whatever Kerberos cryptosystem you're using (defined by
     * the encryption type).  Note that this is NOT the EncryptedData
     * sequence - it's what goes in the "cipher" field of EncryptedData.
     */

    if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {

        if ((current = asn_parse_sequence(current, &length, &type,
                                          (ASN_UNIVERSAL | ASN_PRIMITIVE |
                                           ASN_OCTET_STR), "ksm pdu")) ==
            NULL) {
            DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failed\n"));
            retval = SNMPERR_ASN_PARSE_ERR;
            goto error;
        }

        /*
         * The PDU is now pointed at by "current", and the length is in
         * "length".
         */

        DEBUGMSGTL(("ksm", "KSM starting sPDU decode\n"));

        /*
         * We need to set up a blank initialization vector for the decryption.
         * Use a block of all zero's (which is dependent on the block size
         * of the encryption method).
         */

#ifdef MIT_NEW_CRYPTO

        retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize);

        if (retcode) {
            DEBUGMSGTL(("ksm",
                        "Unable to determine crypto block size: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }
#else                           /* MIT_NEW_CRYPTO */

        blocksize =
            krb5_enctype_array[subkey->enctype]->system->block_length;

#endif                          /* MIT_NEW_CRYPTO */

        ivector.data = malloc(blocksize);

        if (!ivector.data) {
            DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n",
                        blocksize));
            retval = SNMPERR_MALLOC;
            goto error;
        }

        ivector.length = blocksize;
        memset(ivector.data, 0, blocksize);

#ifndef MIT_NEW_CRYPTO

        krb5_use_enctype(kcontext, &eblock, subkey->enctype);

        retcode = krb5_process_key(kcontext, &eblock, subkey);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM key post-processing failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }
#endif                          /* !MIT_NEW_CRYPTO */

        if (length > *parms->scopedPduLen) {
            DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to "
                        "decrypt but only %d bytes available\n", length,
                        *parms->scopedPduLen));
            retval = SNMPERR_TOO_LONG;
#ifndef MIT_NEW_CRYPTO
            krb5_finish_key(kcontext, &eblock);
#endif                          /* ! MIT_NEW_CRYPTO */
            goto error;
        }
#ifdef MIT_NEW_CRYPTO
        in_crypt.ciphertext.data = (char *) current;
        in_crypt.ciphertext.length = length;
        in_crypt.enctype = subkey->enctype;
        output.data = (char *) *parms->scopedPdu;
        output.length = *parms->scopedPduLen;

        retcode =
            krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
                           &ivector, &in_crypt, &output);
#else                           /* MIT_NEW_CRYPTO */

        retcode = krb5_decrypt(kcontext, (krb5_pointer) current,
                               *parms->scopedPdu, length, &eblock,
                               ivector.data);

        krb5_finish_key(kcontext, &eblock);

#endif                          /* MIT_NEW_CRYPTO */

        if (retcode) {
            DEBUGMSGTL(("ksm", "Decryption failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

        *parms->scopedPduLen = length;

    } else {
        /*
         * Clear PDU
         */

        *parms->scopedPdu = current;
        *parms->scopedPduLen =
            parms->wholeMsgLen - (current - parms->wholeMsg);
    }

    /*
     * A HUGE GROSS HACK
     */

    *parms->maxSizeResponse = parms->maxMsgSize - 200;

    DEBUGMSGTL(("ksm", "KSM processing complete\n"));

    /*
     * Set the secName to the right value (a hack for now).  But that's
     * only used for when we're processing a request, not a response.
     */

    if (!response) {

        retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client,
                                    &cname);

        if (retcode) {
            DEBUGMSGTL(("ksm", "KSM krb5_unparse_name failed: %s\n",
                        error_message(retcode)));
            snmp_set_detail(error_message(retcode));
            retval = SNMPERR_KRB5;
            goto error;
        }

        if (strlen(cname) > *parms->secNameLen + 1) {
            DEBUGMSGTL(("ksm",
                        "KSM: Principal length (%d) is too long (%d)\n",
                        strlen(cname), parms->secNameLen));
            retval = SNMPERR_TOO_LONG;
            free(cname);
            goto error;
        }

        strcpy(parms->secName, cname);
        *parms->secNameLen = strlen(cname);

        free(cname);

        /*
         * Also, if we're not a response, keep around our auth_context so we
         * can encode the reply message correctly
         */

        ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef);

        if (!ksm_state) {
            DEBUGMSGTL(("ksm", "KSM unable to malloc memory for "
                        "ksm_secStateRef\n"));
            retval = SNMPERR_MALLOC;
            goto error;
        }

        ksm_state->auth_context = auth_context;
        auth_context = NULL;
        ksm_state->cksumtype = cksumtype;

        *parms->secStateRef = ksm_state;
    } else {

        /*
         * We _still_ have to set the secName in process_in_msg().  Do
         * that now with what we were passed in before (we cached it,
         * remember?)
         */

        memcpy(parms->secName, entry->secName, entry->secNameLen);
        *parms->secNameLen = entry->secNameLen;
    }

    /*
     * Just in case
     */

    parms->secEngineID = (u_char *) "";
    *parms->secEngineIDLen = 0;

    auth_context = NULL;        /* So we don't try to free it on success */

  error:
    if (retval == SNMPERR_ASN_PARSE_ERR &&
        snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0)
        DEBUGMSGTL(("ksm", "Failed to increment statistics.\n"));

    if (subkey)
        krb5_free_keyblock(kcontext, subkey);

    if (checksum.contents)
        free(checksum.contents);

    if (ivector.data)
        free(ivector.data);

    if (ticket)
        krb5_free_ticket(kcontext, ticket);

    if (!response && auth_context)
        krb5_auth_con_free(kcontext, auth_context);

    if (ap_req.data)
        free(ap_req.data);

    return retval;
}