Пример #1
0
static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security, 
				     TALLOC_CTX *mem_ctx, 
				     const DATA_BLOB *in, 
				     DATA_BLOB *out)
{
	struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
	krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
	krb5_auth_context auth_context = gensec_krb5_state->auth_context;
	krb5_error_code ret;
	krb5_data input, output;
	krb5_replay_data replay;
	input.length = in->length;
	input.data = in->data;
	
	if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
		ret = krb5_rd_priv(context, auth_context, &input, &output, &replay);
		if (ret) {
			DEBUG(1, ("krb5_rd_priv failed: %s\n", 
				  smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
							     ret, mem_ctx)));
			return NT_STATUS_ACCESS_DENIED;
		}
		*out = data_blob_talloc(mem_ctx, output.data, output.length);
		
		krb5_data_free(&output);
	} else {
		return NT_STATUS_ACCESS_DENIED;
	}
	return NT_STATUS_OK;
}
Пример #2
0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_read_priv_message(krb5_context context,
		       krb5_auth_context ac,
		       krb5_pointer p_fd,
		       krb5_data *data)
{
    krb5_error_code ret;
    krb5_data packet;

    ret = krb5_read_message(context, p_fd, &packet);
    if(ret)
	return ret;
    ret = krb5_rd_priv (context, ac, &packet, data, NULL);
    krb5_data_free(&packet);
    return ret;
}
Пример #3
0
kadm5_ret_t
_kadm5_client_recv(kadm5_client_context *context, krb5_data *reply)
{
    krb5_error_code ret;
    krb5_data data;
    krb5_storage *sock;

    sock = krb5_storage_from_fd(context->sock);
    if(sock == NULL)
	return ENOMEM;
    ret = krb5_ret_data(sock, &data);
    krb5_storage_free(sock);
    if(ret == KRB5_CC_END)
	return KADM5_RPC_ERROR;
    else if(ret)
	return ret;

    ret = krb5_rd_priv(context->context, context->ac, &data, reply, NULL);
    krb5_data_free(&data);
    return ret;
}
Пример #4
0
int do_krb5_comm(krb5_context context, krb5_keytab keytab, krb5_principal server, char *cmddir) {
	struct sockaddr_in c_saddr, s_saddr;
	socklen_t namelen;
	int sock = 0;
	int len;
	char buff[BUFFSIZE];
	char *cname = NULL;
	krb5_error_code retval;
	krb5_data kdata, message;
	krb5_auth_context auth_context = NULL;
	krb5_ticket *ticket;
	krb5_address ckaddr, skaddr;
	krb5_rcache rcache;
	krb5_data rcache_name;
	long srand, rrand;
	int fd[2];
	char rcname_piece[RC_PIECE_MAXLEN];

	namelen = sizeof(c_saddr);
	if (getpeername(sock, (struct sockaddr *)&c_saddr, &namelen) < 0) {
		syslog(LOG_ERR, "getpeername: %m");
		return 1;
	}

	namelen = sizeof(s_saddr);
	if (getsockname(sock, (struct sockaddr *)&s_saddr, &namelen) < 0) {
		syslog(LOG_ERR, "getsockname: %m");
		return 1;
	}

	/* INIT MSG = random number */
	srand = random();

	/* Send it */
	if (send(sock, &srand, sizeof(srand), 0) < 0) {
		syslog(LOG_ERR, "%m while sending init message");
		return 1;
	}
	if (recv(sock, &rrand, sizeof(rrand), 0) < 0) {
		syslog(LOG_ERR, "%m while receiving init reply");
		return 1;
	}

	/* Reply should contain the same message (number) */
	if (srand != rrand) {
		syslog(LOG_ERR, "Bad init reply");
		return 1;
	}

	/* Do authentication */
	if (retval = krb5_recvauth(context, &auth_context, (krb5_pointer)&sock, AFSADM_VERSION, server, 0, keytab, &ticket)) {
		syslog(LOG_ERR, "recvauth failed: %s", error_message(retval));
		exit(1);
	}

	/* Get client name */
	if (retval = krb5_unparse_name(context, ticket->enc_part2->client, &cname)) {
		syslog(LOG_ERR, "unparse failed: %s", error_message(retval));
		return 1;
	}

	if (ticket)
		krb5_free_ticket(context, ticket);

	if (debug)
		syslog(LOG_DEBUG, "Principal %s", cname);

	/*******************************************************************/

	ckaddr.addrtype = ADDRTYPE_IPPORT;
	ckaddr.length   = sizeof(c_saddr.sin_port);
	ckaddr.contents = (krb5_octet *)&c_saddr.sin_port;

	skaddr.addrtype = ADDRTYPE_IPPORT;
	skaddr.length   = sizeof(s_saddr.sin_port);
	skaddr.contents = (krb5_octet *)&s_saddr.sin_port;
	if ((retval = krb5_auth_con_setports(context, auth_context, &skaddr, &ckaddr))) {
		syslog(LOG_ERR, "%s while setting ports", error_message(retval));
		return 1;
	}

	/* Set foreign_addr for rd_priv() */
	ckaddr.addrtype = ADDRTYPE_INET;
	ckaddr.length   = sizeof(c_saddr.sin_addr);
	ckaddr.contents = (krb5_octet *)&c_saddr.sin_addr;

	/* Set local_addr  */
	skaddr.addrtype = ADDRTYPE_INET;
	skaddr.length   = sizeof(s_saddr.sin_addr);
	skaddr.contents = (krb5_octet *)&s_saddr.sin_addr;

	if ((retval = krb5_auth_con_setaddrs(context, auth_context, &skaddr, &ckaddr))) {
		syslog(LOG_ERR, "%s while setting addrs", error_message(retval));
		return 1;
	}

	/* Receive a request */
	if ((len = recv(sock, (char *)buff, sizeof(buff), 0)) < 0) {
		syslog(LOG_ERR, "%m while receiving datagram");
		return 1;
	}

	kdata.length = len;
	kdata.data = buff;

	if (debug)
		syslog(LOG_DEBUG, "Received %d bytes", len);

	/* Decrypt it */
	if ((retval = krb5_rd_priv(context, auth_context, &kdata, &message, NULL))) {
		syslog(LOG_ERR, "%s while verifying PRIV message", error_message(retval));
		return 1;
	}

	if (message.length > 0) {
#ifdef __osf__
		sprintf(rcname_piece, "afsadmd_%d",  getpid());
#else
		snprintf(rcname_piece, RC_PIECE_MAXLEN, "afsadmd_%d", getpid());
#endif
		rcache_name.data = rcname_piece;
		rcache_name.length = strlen(rcache_name.data);

		if ((retval = krb5_get_server_rcache(context, &rcache_name, &rcache))) {
			syslog(LOG_ERR, "%s while getting server rcache", error_message(retval));
			return 1;
		}

		/* set auth_context rcache */
		if (retval = krb5_auth_con_setrcache(context, auth_context, rcache)) {
			syslog(LOG_ERR, "%s while setting rcache", error_message(retval));
			return 1;
		}

		/*********************************************************************
		 * Call the desired command, read stdout/stderr, send it
		 *********************************************************************/

		/* create fork */
		if (pipe(fd) == -1)
			printf("Failed create fork with pipe().\n");

		if (fork() == 0) {
			close(fd[0]);
			close(1);
			close(2);
			dup2(fd[1], 1);
			dup2(fd[1], 2);

			/* Call required command */
			do_command(context, keytab, server, cname, message.data, cmddir );
			krb5_xfree(message.data);
			exit(0);
		} else {
			/* Read stdout/stderr from pipe, store it to the buffer, encrypt it a send to the client */
			krb5_data message, kdata;
			char buff[PIPEBUFF];
			int n = 0;
			int len = 0;
			int sent = 0;
			int counter = 0;
			int end = 0;
			short netlen;
			time_t starttime, oldtime, newtime;
			FILE *pipedes;

			close(fd[1]);
			pipedes = fdopen(fd[0], "r");

			starttime = oldtime = time(NULL);

			for (n = 0; end == 0; ) {
				/* Read line from pipe */
				if (fgets(buff + n, PIPEBUFF - n, pipedes) == NULL)
					end++;
				else
					n = strlen(buff);

				/* Get time */
				newtime = time(NULL);

				/* Send buffer when
				 *    a) buffer is full
				 *    b) buffer contains data and
				 *      1) end-of-file encountered (end flag)
				 *      2) buffer sent before 1s
				 */
				if ((n == PIPEBUFF) || (((newtime > oldtime) || end ) && (n != 0))) {
					/* Prepare data for sending */
					message.data = buff;
					message.length = n;
					kdata.data = NULL;

					/* Make the encrypted message */
					if ((retval = krb5_mk_priv(context, auth_context, &message, &kdata, NULL))) {
						syslog(LOG_ERR, "%s while making KRB_PRIV message", error_message(retval));
						return 1;
					}

					/* Convert byte order */
					netlen = htons((short)kdata.length);

					/* Send len of encrypted data */
					if ((len = send(sock, (char *)&netlen, sizeof(netlen), 0)) != sizeof(netlen)) {
						krb5_xfree(kdata.data);
						syslog(LOG_ERR, "%m while sending len of PRIV message");
						return 1;
					}

					/* Send it */
					if ((len = send(sock, (char *)kdata.data, kdata.length, 0)) != kdata.length) {
						syslog(LOG_ERR, "%m while sending PRIV message");
						krb5_xfree(kdata.data);
						return 1;
					}

					/* Statistics */
					sent += len;
					counter++;

					/* Timestanmp */
					oldtime = newtime;
					n = 0;

					krb5_xfree(kdata.data);
				}
			}

			newtime = time(NULL);

			if (debug)
				syslog(LOG_DEBUG, "Sent %d bytes in %ds [%d fragment(s)]", sent, (int)(newtime - starttime),  counter);
		}
	}

	//FIXME: There is no way to close or destroy rcache declared in krb5 headers
	//krb5_rc_destroy(context, rcache);

	/* set auth_context rcache */
	if (retval = krb5_auth_con_setrcache(context, auth_context, rcache)) {
		syslog(LOG_ERR, "%s while setting rcache to NULL", error_message(retval));
		return 1;
	}

	free(cname);
	krb5_auth_con_free(context, auth_context);
	return 0;
}
Пример #5
0
static krb5_error_code
process_reply (krb5_context context,
	       krb5_auth_context auth_context,
	       int is_stream,
	       int sock,
	       int *result_code,
	       krb5_data *result_code_string,
	       krb5_data *result_string,
	       const char *host)
{
    krb5_error_code ret;
    u_char reply[1024 * 3];
    ssize_t len;
    u_int16_t pkt_len, pkt_ver;
    krb5_data ap_rep_data;
    int save_errno;

    len = 0;
    if (is_stream) {
	while (len < sizeof(reply)) {
	    unsigned long size;

	    ret = recvfrom (sock, reply + len, sizeof(reply) - len, 
			    0, NULL, NULL);
	    if (ret < 0) {
		save_errno = errno;
		krb5_set_error_string(context, "recvfrom %s: %s",
				      host, strerror(save_errno));
		return save_errno;
	    } else if (ret == 0) {
		krb5_set_error_string(context, "recvfrom timeout %s", host);
		return 1;
	    }
	    len += ret;
	    if (len < 4)
		continue;
	    _krb5_get_int(reply, &size, 4);
	    if (size + 4 < len)
		continue;
	    memmove(reply, reply + 4, size);		
	    len = size;
	    break;
	}
	if (len == sizeof(reply)) {
	    krb5_set_error_string(context, "message too large from %s",
				  host);
	    return ENOMEM;
	}
    } else {
	ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL);
	if (ret < 0) {
	    save_errno = errno;
	    krb5_set_error_string(context, "recvfrom %s: %s",
				  host, strerror(save_errno));
	    return save_errno;
	}
	len = ret;
    }

    if (len < 6) {
	str2data (result_string, "server %s sent to too short message "
		  "(%ld bytes)", host, (long)len);
	*result_code = KRB5_KPASSWD_MALFORMED;
	return 0;
    }

    pkt_len = (reply[0] << 8) | (reply[1]);
    pkt_ver = (reply[2] << 8) | (reply[3]);

    if ((pkt_len != len) || (reply[1] == 0x7e || reply[1] == 0x5e)) {
	KRB_ERROR error;
	size_t size;
	u_char *p;

	memset(&error, 0, sizeof(error));

	ret = decode_KRB_ERROR(reply, len, &error, &size);
	if (ret)
	    return ret;

	if (error.e_data->length < 2) {
	    str2data(result_string, "server %s sent too short "
		     "e_data to print anything usable", host);
	    free_KRB_ERROR(&error);
	    *result_code = KRB5_KPASSWD_MALFORMED;
	    return 0;
	}

	p = error.e_data->data;
	*result_code = (p[0] << 8) | p[1];
	if (error.e_data->length == 2)
	    str2data(result_string, "server only sent error code");
	else 
	    krb5_data_copy (result_string,
			    p + 2,
			    error.e_data->length - 2);
	free_KRB_ERROR(&error);
	return 0;
    }

    if (pkt_len != len) {
	str2data (result_string, "client: wrong len in reply");
	*result_code = KRB5_KPASSWD_MALFORMED;
	return 0;
    }
    if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) {
	str2data (result_string,
		  "client: wrong version number (%d)", pkt_ver);
	*result_code = KRB5_KPASSWD_MALFORMED;
	return 0;
    }

    ap_rep_data.data = reply + 6;
    ap_rep_data.length  = (reply[4] << 8) | (reply[5]);
  
    if (reply + len < (u_char *)ap_rep_data.data + ap_rep_data.length) {
	str2data (result_string, "client: wrong AP len in reply");
	*result_code = KRB5_KPASSWD_MALFORMED;
	return 0;
    }

    if (ap_rep_data.length) {
	krb5_ap_rep_enc_part *ap_rep;
	krb5_data priv_data;
	u_char *p;

	priv_data.data   = (u_char*)ap_rep_data.data + ap_rep_data.length;
	priv_data.length = len - ap_rep_data.length - 6;

	ret = krb5_rd_rep (context,
			   auth_context,
			   &ap_rep_data,
			   &ap_rep);
	if (ret)
	    return ret;

	krb5_free_ap_rep_enc_part (context, ap_rep);

	ret = krb5_rd_priv (context,
			    auth_context,
			    &priv_data,
			    result_code_string,
			    NULL);
	if (ret) {
	    krb5_data_free (result_code_string);
	    return ret;
	}

	if (result_code_string->length < 2) {
	    *result_code = KRB5_KPASSWD_MALFORMED;
	    str2data (result_string,
		      "client: bad length in result");
	    return 0;
	}

        p = result_code_string->data;
      
        *result_code = (p[0] << 8) | p[1];
        krb5_data_copy (result_string,
                        (unsigned char*)result_code_string->data + 2,
                        result_code_string->length - 2);
        return 0;
    } else {
	KRB_ERROR error;
	size_t size;
	u_char *p;
      
	ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size);
	if (ret) {
	    return ret;
	}
	if (error.e_data->length < 2) {
	    krb5_warnx (context, "too short e_data to print anything usable");
	    return 1;		/* XXX */
	}

	p = error.e_data->data;
	*result_code = (p[0] << 8) | p[1];
	krb5_data_copy (result_string,
			p + 2,
			error.e_data->length - 2);
	return 0;
    }
}
Пример #6
0
static krb5_error_code parse_setpw_reply(krb5_context context,
					 bool use_tcp,
					 krb5_auth_context auth_context,
					 krb5_data *packet)
{
	krb5_data ap_rep;
	char *p;
	int vnum, ret, res_code;
	krb5_data cipherresult;
	krb5_data clearresult;
	krb5_ap_rep_enc_part *ap_rep_enc;
	krb5_replay_data replay;
	unsigned int msg_length = packet->length;


	if (packet->length < (use_tcp ? 8 : 4)) {
		return KRB5KRB_AP_ERR_MODIFIED;
	}
	
	p = (char *)packet->data;
	/*
	** see if it is an error
	*/
	if (krb5_is_krb_error(packet)) {

		ret = handle_krberror_packet(context, packet);
		if (ret) {
			return ret;
		}
	}

	
	/* tcp... */
	if (use_tcp) {
		msg_length -= 4;
		if (RIVAL(p, 0) != msg_length) {
			DEBUG(1,("Bad TCP packet length (%d/%d) from kpasswd server\n",
			RIVAL(p, 0), msg_length));
			return KRB5KRB_AP_ERR_MODIFIED;
		}

		p += 4;
	}
	
	if (RSVAL(p, 0) != msg_length) {
		DEBUG(1,("Bad packet length (%d/%d) from kpasswd server\n",
			 RSVAL(p, 0), msg_length));
		return KRB5KRB_AP_ERR_MODIFIED;
	}

	p += 2;

	vnum = RSVAL(p, 0); p += 2;

	/* FIXME: According to standard there is only one type of reply */	
	if (vnum != KRB5_KPASSWD_VERS_SETPW && 
	    vnum != KRB5_KPASSWD_VERS_SETPW_ALT && 
	    vnum != KRB5_KPASSWD_VERS_CHANGEPW) {
		DEBUG(1,("Bad vnum (%d) from kpasswd server\n", vnum));
		return KRB5KDC_ERR_BAD_PVNO;
	}
	
	ap_rep.length = RSVAL(p, 0); p += 2;
	
	if (p + ap_rep.length >= (char *)packet->data + packet->length) {
		DEBUG(1,("ptr beyond end of packet from kpasswd server\n"));
		return KRB5KRB_AP_ERR_MODIFIED;
	}
	
	if (ap_rep.length == 0) {
		DEBUG(1,("got unencrypted setpw result?!\n"));
		return KRB5KRB_AP_ERR_MODIFIED;
	}

	/* verify ap_rep */
	ap_rep.data = p;
	p += ap_rep.length;
	
	ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
	if (ret) {
		DEBUG(1,("failed to rd setpw reply (%s)\n", error_message(ret)));
		return KRB5KRB_AP_ERR_MODIFIED;
	}
	
	krb5_free_ap_rep_enc_part(context, ap_rep_enc);
	
	cipherresult.data = p;
	cipherresult.length = ((char *)packet->data + packet->length) - p;
		
	ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
			   &replay);
	if (ret) {
		DEBUG(1,("failed to decrypt setpw reply (%s)\n", error_message(ret)));
		return KRB5KRB_AP_ERR_MODIFIED;
	}

	if (clearresult.length < 2) {
	        free(clearresult.data);
		ret = KRB5KRB_AP_ERR_MODIFIED;
		return KRB5KRB_AP_ERR_MODIFIED;
	}
	
	p = (char *)clearresult.data;
	
	res_code = RSVAL(p, 0);
	
	free(clearresult.data);

	if ((res_code < KRB5_KPASSWD_SUCCESS) || 
	    (res_code > KRB5_KPASSWD_ETYPE_NOSUPP)) {
		return KRB5KRB_AP_ERR_MODIFIED;
	}

	if (res_code == KRB5_KPASSWD_SUCCESS) {
		return 0;
	} else {
		const char *errstr;
		setpw_result_code_string(context, res_code, &errstr);
		DEBUG(1, ("Error changing password: %s (%d)\n", errstr, res_code));

		return kpasswd_err_to_krb5_err(res_code);
	}
}
Пример #7
0
static int
proto (int sock, const char *service)
{
    krb5_auth_context auth_context;
    krb5_error_code status;
    krb5_principal server;
    krb5_ticket *ticket;
    char *name;
    char hostname[MAXHOSTNAMELEN];
    krb5_data packet;
    krb5_data data;
    u_int32_t len, net_len;
    ssize_t n;

    status = krb5_auth_con_init (context, &auth_context);
    if (status)
	krb5_err (context, 1, status, "krb5_auth_con_init");

    status = krb5_auth_con_setaddrs_from_fd (context,
					     auth_context,
					     &sock);

    if (status)
	krb5_err (context, 1, status, "krb5_auth_con_setaddrs_from_fd");

    if(gethostname (hostname, sizeof(hostname)) < 0)
	krb5_err (context, 1, errno, "gethostname");

    status = krb5_sname_to_principal (context,
				      hostname,
				      service,
				      KRB5_NT_SRV_HST,
				      &server);
    if (status)
	krb5_err (context, 1, status, "krb5_sname_to_principal");

    status = krb5_recvauth (context,
			    &auth_context,
			    &sock,
			    VERSION,
			    server,
			    0,
			    NULL,
			    &ticket);
    if (status)
	krb5_err (context, 1, status, "krb5_recvauth");

    status = krb5_unparse_name (context,
				ticket->client,
				&name);
    if (status)
	krb5_err (context, 1, status, "krb5_unparse_name");

    fprintf (stderr, "User is `%s'\n", name);
    free (name);

    krb5_data_zero (&data);
    krb5_data_zero (&packet);

    n = krb5_net_read (context, &sock, &net_len, 4);
    if (n == 0)
	krb5_errx (context, 1, "EOF in krb5_net_read");
    if (n < 0)
	krb5_err (context, 1, errno, "krb5_net_read");

    len = ntohl(net_len);

    krb5_data_alloc (&packet, len);

    n = krb5_net_read (context, &sock, packet.data, len);
    if (n == 0)
	krb5_errx (context, 1, "EOF in krb5_net_read");
    if (n < 0)
	krb5_err (context, 1, errno, "krb5_net_read");
    
    status = krb5_rd_safe (context,
			   auth_context,
			   &packet,
			   &data,
			   NULL);
    if (status)
	krb5_err (context, 1, status, "krb5_rd_safe");

    fprintf (stderr, "safe packet: %.*s\n", (int)data.length,
	    (char *)data.data);

    n = krb5_net_read (context, &sock, &net_len, 4);
    if (n == 0)
	krb5_errx (context, 1, "EOF in krb5_net_read");
    if (n < 0)
	krb5_err (context, 1, errno, "krb5_net_read");

    len = ntohl(net_len);

    krb5_data_alloc (&packet, len);

    n = krb5_net_read (context, &sock, packet.data, len);
    if (n == 0)
	krb5_errx (context, 1, "EOF in krb5_net_read");
    if (n < 0)
	krb5_err (context, 1, errno, "krb5_net_read");
    
    status = krb5_rd_priv (context,
			   auth_context,
			   &packet,
			   &data,
			   NULL);
    if (status)
	krb5_err (context, 1, status, "krb5_rd_priv");

    fprintf (stderr, "priv packet: %.*s\n", (int)data.length,
	    (char *)data.data);

    return 0;
}
Пример #8
0
static int
proto (int sock, const char *service)
{
    struct sockaddr_in remote, local;
    socklen_t addrlen;
    krb5_address remote_addr, local_addr;
    krb5_ccache ccache;
    krb5_auth_context auth_context;
    krb5_error_code status;
    krb5_data packet;
    krb5_data data;
    krb5_data client_name;
    krb5_creds in_creds, *out_creds;

    addrlen = sizeof(local);
    if (getsockname (sock, (struct sockaddr *)&local, &addrlen) < 0
	|| addrlen != sizeof(local))
	err (1, "getsockname)");

    addrlen = sizeof(remote);
    if (getpeername (sock, (struct sockaddr *)&remote, &addrlen) < 0
	|| addrlen != sizeof(remote))
	err (1, "getpeername");

    status = krb5_auth_con_init (context, &auth_context);
    if (status)
	errx (1, "krb5_auth_con_init: %s",
	      krb5_get_err_text(context, status));

    local_addr.addr_type = AF_INET;
    local_addr.address.length = sizeof(local.sin_addr);
    local_addr.address.data   = &local.sin_addr;

    remote_addr.addr_type = AF_INET;
    remote_addr.address.length = sizeof(remote.sin_addr);
    remote_addr.address.data   = &remote.sin_addr;

    status = krb5_auth_con_setaddrs (context,
				     auth_context,
				     &local_addr,
				     &remote_addr);
    if (status)
	errx (1, "krb5_auth_con_setaddr: %s",
	      krb5_get_err_text(context, status));

    status = krb5_read_message(context, &sock, &client_name);
    if(status)
	krb5_err(context, 1, status, "krb5_read_message");

    memset(&in_creds, 0, sizeof(in_creds));
    status = krb5_cc_default(context, &ccache);
    if(status)
	krb5_err(context, 1, status, "krb5_cc_default");
    status = krb5_cc_get_principal(context, ccache, &in_creds.client);
    if(status)
	krb5_err(context, 1, status, "krb5_cc_get_principal");

    status = krb5_read_message(context, &sock, &in_creds.second_ticket);
    if(status)
	krb5_err(context, 1, status, "krb5_read_message");

    status = krb5_parse_name(context, client_name.data, &in_creds.server);
    if(status)
	krb5_err(context, 1, status, "krb5_parse_name");

    status = krb5_get_credentials(context, KRB5_GC_USER_USER, ccache,
				  &in_creds, &out_creds);
    if(status)
	krb5_err(context, 1, status, "krb5_get_credentials");

    status = krb5_cc_default(context, &ccache);
    if(status)
	krb5_err(context, 1, status, "krb5_cc_default");

    status = krb5_sendauth(context,
			   &auth_context,
			   &sock,
			   VERSION,
			   in_creds.client,
			   in_creds.server,
			   AP_OPTS_USE_SESSION_KEY,
			   NULL,
			   out_creds,
			   ccache,
			   NULL,
			   NULL,
			   NULL);
			
    if (status)
	krb5_err(context, 1, status, "krb5_sendauth");

    {
	char *str;
	krb5_unparse_name(context, in_creds.server, &str);
	printf ("User is `%s'\n", str);
	free(str);
	krb5_unparse_name(context, in_creds.client, &str);
	printf ("Server is `%s'\n", str);
	free(str);
    }

    krb5_data_zero (&data);
    krb5_data_zero (&packet);

    status = krb5_read_message(context, &sock, &packet);
    if(status)
	krb5_err(context, 1, status, "krb5_read_message");

    status = krb5_rd_safe (context,
			   auth_context,
			   &packet,
			   &data,
			   NULL);
    if (status)
	errx (1, "krb5_rd_safe: %s",
	      krb5_get_err_text(context, status));

    printf ("safe packet: %.*s\n", (int)data.length,
	    (char *)data.data);

    status = krb5_read_message(context, &sock, &packet);
    if(status)
	krb5_err(context, 1, status, "krb5_read_message");

    status = krb5_rd_priv (context,
			   auth_context,
			   &packet,
			   &data,
			   NULL);
    if (status)
	errx (1, "krb5_rd_priv: %s",
	      krb5_get_err_text(context, status));

    printf ("priv packet: %.*s\n", (int)data.length,
	    (char *)data.data);

    return 0;
}
Пример #9
0
static krb5_error_code
process_reply(krb5_context context,
              krb5_auth_context auth_context,
              krb5_data *data,
              int *result_code,
              krb5_data *result_code_string,
              krb5_data *result_string)
{
    krb5_error_code ret;
    ssize_t len;
    uint16_t pkt_len, pkt_ver;
    krb5_data ap_rep_data;
    uint8_t *reply;

    krb5_auth_con_clear(context, auth_context,
                        KRB5_AUTH_CONTEXT_CLEAR_LOCAL_ADDR|KRB5_AUTH_CONTEXT_CLEAR_REMOTE_ADDR);
    len = data->length;
    reply = data->data;;

    if (len < 6) {
        krb5_data_format(result_string, "server sent to too short message "
                         "(%ld bytes)", (long)len);
        *result_code = KRB5_KPASSWD_MALFORMED;
        return 0;
    }

    pkt_len = (reply[0] << 8) | (reply[1]);
    pkt_ver = (reply[2] << 8) | (reply[3]);

    if ((pkt_len != len) || (reply[1] == 0x7e || reply[1] == 0x5e)) {
        KRB_ERROR error;
        size_t size;
        u_char *p;

        memset(&error, 0, sizeof(error));

        ret = decode_KRB_ERROR(reply, len, &error, &size);
        if (ret)
            return ret;

        if (error.e_data->length < 2) {
            krb5_data_format(result_string, "server sent too short "
                             "e_data to print anything usable");
            free_KRB_ERROR(&error);
            *result_code = KRB5_KPASSWD_MALFORMED;
            return 0;
        }

        p = error.e_data->data;
        *result_code = (p[0] << 8) | p[1];
        if (error.e_data->length == 2)
            krb5_data_format(result_string, "server only sent error code");
        else
            krb5_data_copy (result_string,
                            p + 2,
                            error.e_data->length - 2);
        free_KRB_ERROR(&error);
        return 0;
    }

    if (pkt_len != len) {
        krb5_data_format(result_string, "client: wrong len in reply");
        *result_code = KRB5_KPASSWD_MALFORMED;
        return 0;
    }
    if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) {
        krb5_data_format(result_string,
                         "client: wrong version number (%d)", pkt_ver);
        *result_code = KRB5_KPASSWD_MALFORMED;
        return 0;
    }

    ap_rep_data.data = reply + 6;
    ap_rep_data.length  = (reply[4] << 8) | (reply[5]);

    if (reply + len < (u_char *)ap_rep_data.data + ap_rep_data.length) {
        krb5_data_format(result_string, "client: wrong AP len in reply");
        *result_code = KRB5_KPASSWD_MALFORMED;
        return 0;
    }

    if (ap_rep_data.length) {
        krb5_ap_rep_enc_part *ap_rep;
        krb5_data priv_data;
        u_char *p;

        priv_data.data   = (u_char*)ap_rep_data.data + ap_rep_data.length;
        priv_data.length = len - ap_rep_data.length - 6;

        ret = krb5_rd_rep (context,
                           auth_context,
                           &ap_rep_data,
                           &ap_rep);
        if (ret)
            return ret;

        krb5_free_ap_rep_enc_part (context, ap_rep);

        ret = krb5_rd_priv (context,
                            auth_context,
                            &priv_data,
                            result_code_string,
                            NULL);
        if (ret) {
            krb5_data_free (result_code_string);
            return ret;
        }

        if (result_code_string->length < 2) {
            *result_code = KRB5_KPASSWD_MALFORMED;
            krb5_data_format(result_string,
                             "client: bad length in result");
            return 0;
        }

        p = result_code_string->data;

        *result_code = (p[0] << 8) | p[1];
        krb5_data_copy (result_string,
                        (unsigned char*)result_code_string->data + 2,
                        result_code_string->length - 2);
        return 0;
    } else {
        KRB_ERROR error;
        size_t size;
        u_char *p;

        ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size);
        if (ret) {
            return ret;
        }
        if (error.e_data->length < 2) {
            krb5_warnx (context, "too short e_data to print anything usable");
            return 1;		/* XXX */
        }

        p = error.e_data->data;
        *result_code = (p[0] << 8) | p[1];
        krb5_data_copy (result_string,
                        p + 2,
                        error.e_data->length - 2);
        return 0;
    }
}
Пример #10
0
static int
verify (krb5_auth_context *auth_context,
        krb5_realm *realms,
        krb5_keytab keytab,
        krb5_ticket **ticket,
        krb5_data *out_data,
        uint16_t *version,
        int s,
        struct sockaddr *sa,
        int sa_size,
        u_char *msg,
        size_t len,
        krb5_address *client_addr)
{
    krb5_error_code ret;
    uint16_t pkt_len, pkt_ver, ap_req_len;
    krb5_data ap_req_data;
    krb5_data krb_priv_data;
    krb5_realm *r;

    /*
     * Only send an error reply if the request passes basic length
     * verification.  Otherwise, kpasswdd would reply to every UDP packet,
     * allowing an attacker to set up a ping-pong DoS attack via a spoofed UDP
     * packet with a source address of another UDP service that also replies
     * to every packet.
     *
     * Also suppress the error reply if ap_req_len is 0, which indicates
     * either an invalid request or an error packet.  An error packet may be
     * the result of a ping-pong attacker pointing us at another kpasswdd.
     */
    pkt_len = (msg[0] << 8) | (msg[1]);
    pkt_ver = (msg[2] << 8) | (msg[3]);
    ap_req_len = (msg[4] << 8) | (msg[5]);
    if (pkt_len != len) {
        krb5_warnx (context, "Strange len: %ld != %ld",
                    (long)pkt_len, (long)len);
        return 1;
    }
    if (ap_req_len == 0) {
        krb5_warnx (context, "Request is error packet (ap_req_len == 0)");
        return 1;
    }
    if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW &&
            pkt_ver != KRB5_KPASSWD_VERS_SETPW) {
        krb5_warnx (context, "Bad version (%d)", pkt_ver);
        reply_error (NULL, s, sa, sa_size, 0, 1, "Wrong program version");
        return 1;
    }
    *version = pkt_ver;

    ap_req_data.data   = msg + 6;
    ap_req_data.length = ap_req_len;

    ret = krb5_rd_req (context,
                       auth_context,
                       &ap_req_data,
                       NULL,
                       keytab,
                       NULL,
                       ticket);
    if (ret) {
        krb5_warn (context, ret, "krb5_rd_req");
        reply_error (NULL, s, sa, sa_size, ret, 3, "Authentication failed");
        return 1;
    }

    /* verify realm and principal */
    for (r = realms; *r != NULL; r++) {
        krb5_principal principal;
        krb5_boolean same;

        ret = krb5_make_principal (context,
                                   &principal,
                                   *r,
                                   "kadmin",
                                   "changepw",
                                   NULL);
        if (ret)
            krb5_err (context, 1, ret, "krb5_make_principal");

        same = krb5_principal_compare(context, principal, (*ticket)->server);
        krb5_free_principal(context, principal);
        if (same == TRUE)
            break;
    }
    if (*r == NULL) {
        char *str;
        krb5_unparse_name(context, (*ticket)->server, &str);
        krb5_warnx (context, "client used not valid principal %s", str);
        free(str);
        reply_error (NULL, s, sa, sa_size, ret, 1,
                     "Bad request");
        goto out;
    }

    if (strcmp((*ticket)->server->realm, (*ticket)->client->realm) != 0) {
        krb5_warnx (context, "server realm (%s) not same a client realm (%s)",
                    (*ticket)->server->realm, (*ticket)->client->realm);
        reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1,
                     "Bad request");
        goto out;
    }

    if (!(*ticket)->ticket.flags.initial) {
        krb5_warnx (context, "initial flag not set");
        reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1,
                     "Bad request");
        goto out;
    }
    krb_priv_data.data   = msg + 6 + ap_req_len;
    krb_priv_data.length = len - 6 - ap_req_len;

    /*
     * Only enforce client addresses on on tickets with addresses.  If
     * its addressless, we are guessing its behind NAT and really
     * can't know this information.
     */

    if ((*ticket)->ticket.caddr && (*ticket)->ticket.caddr->len > 0) {
        ret = krb5_auth_con_setaddrs (context, *auth_context,
                                      NULL, client_addr);
        if (ret) {
            krb5_warn (context, ret, "krb5_auth_con_setaddr(this)");
            goto out;
        }
    }

    ret = krb5_rd_priv (context,
                        *auth_context,
                        &krb_priv_data,
                        out_data,
                        NULL);

    if (ret) {
        krb5_warn (context, ret, "krb5_rd_priv");
        reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 3,
                     "Bad request");
        goto out;
    }
    return 0;
out:
    krb5_free_ticket (context, *ticket);
    ticket = NULL;
    return 1;
}
Пример #11
0
/* Decode a reply to produce the clear-text output. */
static krb5_error_code
get_clear_result(krb5_context context, krb5_auth_context auth_context,
                 const krb5_data *packet, krb5_data **clear_out,
                 krb5_boolean *is_error_out)
{
    krb5_error_code ret;
    char *ptr, *end = packet->data + packet->length;
    unsigned int plen, vno, aplen;
    krb5_data ap_rep, cipher, error;
    krb5_ap_rep_enc_part *ap_rep_enc;
    krb5_replay_data replay;
    krb5_key send_subkey = NULL;
    krb5_data clear = empty_data();

    *clear_out = NULL;
    *is_error_out = FALSE;

    /* Check for an unframed KRB-ERROR (expected for RFC 3244 requests; also
     * received from MS AD for version 1 requests). */
    if (krb5_is_krb_error(packet)) {
        *is_error_out = TRUE;
        return get_error_edata(context, packet, clear_out);
    }

    if (packet->length < 6)
        return KRB5KRB_AP_ERR_MODIFIED;

    /* Decode and verify the length. */
    ptr = packet->data;
    plen = (*ptr++ & 0xff);
    plen = (plen << 8) | (*ptr++ & 0xff);
    if (plen != packet->length)
        return KRB5KRB_AP_ERR_MODIFIED;

    /* Decode and verify the version number. */
    vno = (*ptr++ & 0xff);
    vno = (vno << 8) | (*ptr++ & 0xff);
    if (vno != 1 && vno != 0xff80)
        return KRB5KDC_ERR_BAD_PVNO;

    /* Decode and check the AP-REP length. */
    aplen = (*ptr++ & 0xff);
    aplen = (aplen << 8) | (*ptr++ & 0xff);
    if (aplen > end - ptr)
        return KRB5KRB_AP_ERR_MODIFIED;

    /* A zero-length AP-REQ indicates a framed KRB-ERROR response.  (Expected
     * for protocol version 1; specified but unusual for RFC 3244 requests.) */
    if (aplen == 0) {
        *is_error_out = TRUE;
        error = make_data(ptr, end - ptr);
        return get_error_edata(context, &error, clear_out);
    }

    /* We have an AP-REP.  Save send_subkey to later smash recv_subkey. */
    ret = krb5_auth_con_getsendsubkey_k(context, auth_context, &send_subkey);
    if (ret)
        return ret;

    /* Verify the AP-REP. */
    ap_rep = make_data(ptr, aplen);
    ptr += ap_rep.length;
    ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
    if (ret)
        goto cleanup;
    krb5_free_ap_rep_enc_part(context, ap_rep_enc);

    /* Smash recv_subkey to be send_subkey, per spec. */
    ret = krb5_auth_con_setrecvsubkey_k(context, auth_context, send_subkey);
    if (ret)
        goto cleanup;

    /* Extract and decrypt the result. */
    cipher = make_data(ptr, end - ptr);
    ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay);
    if (ret)
        goto cleanup;

    ret = krb5_copy_data(context, &clear, clear_out);
    if (ret)
        goto cleanup;
    *is_error_out = FALSE;

cleanup:
    krb5_k_free_key(context, send_subkey);
    krb5_free_data_contents(context, &clear);
    return ret;
}
Пример #12
0
static krb5_error_code
process_chpw_request(krb5_context context, void *server_handle, char *realm,
                     krb5_keytab keytab, const krb5_fulladdr *local_faddr,
                     const krb5_fulladdr *remote_faddr, krb5_data *req,
                     krb5_data *rep)
{
    krb5_error_code ret;
    char *ptr;
    unsigned int plen, vno;
    krb5_data ap_req, ap_rep = empty_data();
    krb5_data cipher = empty_data(), clear = empty_data();
    krb5_auth_context auth_context = NULL;
    krb5_principal changepw = NULL;
    krb5_principal client, target = NULL;
    krb5_ticket *ticket = NULL;
    krb5_replay_data replay;
    krb5_error krberror;
    int numresult;
    char strresult[1024];
    char *clientstr = NULL, *targetstr = NULL;
    const char *errmsg = NULL;
    size_t clen;
    char *cdots;
    struct sockaddr_storage ss;
    socklen_t salen;
    char addrbuf[100];
    krb5_address *addr = remote_faddr->address;

    *rep = empty_data();

    if (req->length < 4) {
        /* either this, or the server is printing bad messages,
           or the caller passed in garbage */
        ret = KRB5KRB_AP_ERR_MODIFIED;
        numresult = KRB5_KPASSWD_MALFORMED;
        strlcpy(strresult, "Request was truncated", sizeof(strresult));
        goto bailout;
    }

    ptr = req->data;

    /* verify length */

    plen = (*ptr++ & 0xff);
    plen = (plen<<8) | (*ptr++ & 0xff);

    if (plen != req->length) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        numresult = KRB5_KPASSWD_MALFORMED;
        strlcpy(strresult, "Request length was inconsistent",
                sizeof(strresult));
        goto bailout;
    }

    /* verify version number */

    vno = (*ptr++ & 0xff) ;
    vno = (vno<<8) | (*ptr++ & 0xff);

    if (vno != 1 && vno != RFC3244_VERSION) {
        ret = KRB5KDC_ERR_BAD_PVNO;
        numresult = KRB5_KPASSWD_BAD_VERSION;
        snprintf(strresult, sizeof(strresult),
                 "Request contained unknown protocol version number %d", vno);
        goto bailout;
    }

    /* read, check ap-req length */

    ap_req.length = (*ptr++ & 0xff);
    ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff);

    if (ptr + ap_req.length >= req->data + req->length) {
        ret = KRB5KRB_AP_ERR_MODIFIED;
        numresult = KRB5_KPASSWD_MALFORMED;
        strlcpy(strresult, "Request was truncated in AP-REQ",
                sizeof(strresult));
        goto bailout;
    }

    /* verify ap_req */

    ap_req.data = ptr;
    ptr += ap_req.length;

    ret = krb5_auth_con_init(context, &auth_context);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed initializing auth context",
                sizeof(strresult));
        goto chpwfail;
    }

    ret = krb5_auth_con_setflags(context, auth_context,
                                 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed initializing auth context",
                sizeof(strresult));
        goto chpwfail;
    }

    ret = krb5_build_principal(context, &changepw, strlen(realm), realm,
                               "kadmin", "changepw", NULL);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed building kadmin/changepw principal",
                sizeof(strresult));
        goto chpwfail;
    }

    ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab,
                      NULL, &ticket);

    if (ret) {
        numresult = KRB5_KPASSWD_AUTHERROR;
        strlcpy(strresult, "Failed reading application request",
                sizeof(strresult));
        goto chpwfail;
    }

    /* construct the ap-rep */

    ret = krb5_mk_rep(context, auth_context, &ap_rep);
    if (ret) {
        numresult = KRB5_KPASSWD_AUTHERROR;
        strlcpy(strresult, "Failed replying to application request",
                sizeof(strresult));
        goto chpwfail;
    }

    /* decrypt the ChangePasswdData */

    cipher.length = (req->data + req->length) - ptr;
    cipher.data = ptr;

    /*
     * Don't set a remote address in auth_context before calling krb5_rd_priv,
     * so that we can work against clients behind a NAT.  Reflection attacks
     * aren't a concern since we use sequence numbers and since our requests
     * don't look anything like our responses.  Also don't set a local address,
     * since we don't know what interface the request was received on.
     */

    ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed decrypting request", sizeof(strresult));
        goto chpwfail;
    }

    client = ticket->enc_part2->client;

    /* decode ChangePasswdData for setpw requests */
    if (vno == RFC3244_VERSION) {
        krb5_data *clear_data;

        ret = decode_krb5_setpw_req(&clear, &clear_data, &target);
        if (ret != 0) {
            numresult = KRB5_KPASSWD_MALFORMED;
            strlcpy(strresult, "Failed decoding ChangePasswdData",
                    sizeof(strresult));
            goto chpwfail;
        }

        zapfree(clear.data, clear.length);

        clear = *clear_data;
        free(clear_data);

        if (target != NULL) {
            ret = krb5_unparse_name(context, target, &targetstr);
            if (ret != 0) {
                numresult = KRB5_KPASSWD_HARDERROR;
                strlcpy(strresult, "Failed unparsing target name for log",
                        sizeof(strresult));
                goto chpwfail;
            }
        }
    }

    ret = krb5_unparse_name(context, client, &clientstr);
    if (ret) {
        numresult = KRB5_KPASSWD_HARDERROR;
        strlcpy(strresult, "Failed unparsing client name for log",
                sizeof(strresult));
        goto chpwfail;
    }

    /* for cpw, verify that this is an AS_REQ ticket */
    if (vno == 1 &&
        (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) {
        numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
        strlcpy(strresult, "Ticket must be derived from a password",
                sizeof(strresult));
        goto chpwfail;
    }

    /* change the password */

    ptr = k5memdup0(clear.data, clear.length, &ret);
    ret = schpw_util_wrapper(server_handle, client, target,
                             (ticket->enc_part2->flags & TKT_FLG_INITIAL) != 0,
                             ptr, NULL, strresult, sizeof(strresult));
    if (ret)
        errmsg = krb5_get_error_message(context, ret);

    /* zap the password */
    zapfree(clear.data, clear.length);
    zapfree(ptr, clear.length);
    clear = empty_data();

    clen = strlen(clientstr);
    trunc_name(&clen, &cdots);

    switch (addr->addrtype) {
    case ADDRTYPE_INET: {
        struct sockaddr_in *sin = ss2sin(&ss);

        sin->sin_family = AF_INET;
        memcpy(&sin->sin_addr, addr->contents, addr->length);
        sin->sin_port = htons(remote_faddr->port);
        salen = sizeof(*sin);
        break;
    }
    case ADDRTYPE_INET6: {
        struct sockaddr_in6 *sin6 = ss2sin6(&ss);

        sin6->sin6_family = AF_INET6;
        memcpy(&sin6->sin6_addr, addr->contents, addr->length);
        sin6->sin6_port = htons(remote_faddr->port);
        salen = sizeof(*sin6);
        break;
    }
    default: {
        struct sockaddr *sa = ss2sa(&ss);

        sa->sa_family = AF_UNSPEC;
        salen = sizeof(*sa);
        break;
    }
    }

    if (getnameinfo(ss2sa(&ss), salen,
                    addrbuf, sizeof(addrbuf), NULL, 0,
                    NI_NUMERICHOST | NI_NUMERICSERV) != 0)
        strlcpy(addrbuf, "<unprintable>", sizeof(addrbuf));

    if (vno == RFC3244_VERSION) {
        size_t tlen;
        char *tdots;
        const char *targetp;

        if (target == NULL) {
            tlen = clen;
            tdots = cdots;
            targetp = targetstr;
        } else {
            tlen = strlen(targetstr);
            trunc_name(&tlen, &tdots);
            targetp = clientstr;
        }

        krb5_klog_syslog(LOG_NOTICE, _("setpw request from %s by %.*s%s for "
                                       "%.*s%s: %s"), addrbuf, (int) clen,
                         clientstr, cdots, (int) tlen, targetp, tdots,
                         errmsg ? errmsg : "success");
    } else {
        krb5_klog_syslog(LOG_NOTICE, _("chpw request from %s for %.*s%s: %s"),
                         addrbuf, (int) clen, clientstr, cdots,
                         errmsg ? errmsg : "success");
    }
    switch (ret) {
    case KADM5_AUTH_CHANGEPW:
        numresult = KRB5_KPASSWD_ACCESSDENIED;
        break;
    case KADM5_PASS_Q_TOOSHORT:
    case KADM5_PASS_REUSE:
    case KADM5_PASS_Q_CLASS:
    case KADM5_PASS_Q_DICT:
    case KADM5_PASS_Q_GENERIC:
    case KADM5_PASS_TOOSOON:
        numresult = KRB5_KPASSWD_SOFTERROR;
        break;
    case 0:
        numresult = KRB5_KPASSWD_SUCCESS;
        strlcpy(strresult, "", sizeof(strresult));
        break;
    default:
        numresult = KRB5_KPASSWD_HARDERROR;
        break;
    }

chpwfail:

    ret = alloc_data(&clear, 2 + strlen(strresult));
    if (ret)
        goto bailout;

    ptr = clear.data;

    *ptr++ = (numresult>>8) & 0xff;
    *ptr++ = numresult & 0xff;

    memcpy(ptr, strresult, strlen(strresult));

    cipher = empty_data();

    if (ap_rep.length) {
        ret = krb5_auth_con_setaddrs(context, auth_context,
                                     local_faddr->address, NULL);
        if (ret) {
            numresult = KRB5_KPASSWD_HARDERROR;
            strlcpy(strresult,
                    "Failed storing client and server internet addresses",
                    sizeof(strresult));
        } else {
            ret = krb5_mk_priv(context, auth_context, &clear, &cipher,
                               &replay);
            if (ret) {
                numresult = KRB5_KPASSWD_HARDERROR;
                strlcpy(strresult, "Failed encrypting reply",
                        sizeof(strresult));
            }
        }
    }

    /* if no KRB-PRIV was constructed, then we need a KRB-ERROR.
       if this fails, just bail.  there's nothing else we can do. */

    if (cipher.length == 0) {
        /* clear out ap_rep now, so that it won't be inserted in the
           reply */

        if (ap_rep.length) {
            free(ap_rep.data);
            ap_rep = empty_data();
        }

        krberror.ctime = 0;
        krberror.cusec = 0;
        krberror.susec = 0;
        ret = krb5_timeofday(context, &krberror.stime);
        if (ret)
            goto bailout;

        /* this is really icky.  but it's what all the other callers
           to mk_error do. */
        krberror.error = ret;
        krberror.error -= ERROR_TABLE_BASE_krb5;
        if (krberror.error < 0 || krberror.error > KRB_ERR_MAX)
            krberror.error = KRB_ERR_GENERIC;

        krberror.client = NULL;

        ret = krb5_build_principal(context, &krberror.server,
                                   strlen(realm), realm,
                                   "kadmin", "changepw", NULL);
        if (ret)
            goto bailout;
        krberror.text.length = 0;
        krberror.e_data = clear;

        ret = krb5_mk_error(context, &krberror, &cipher);

        krb5_free_principal(context, krberror.server);

        if (ret)
            goto bailout;
    }

    /* construct the reply */

    ret = alloc_data(rep, 6 + ap_rep.length + cipher.length);
    if (ret)
        goto bailout;
    ptr = rep->data;

    /* length */

    *ptr++ = (rep->length>>8) & 0xff;
    *ptr++ = rep->length & 0xff;

    /* version == 0x0001 big-endian */

    *ptr++ = 0;
    *ptr++ = 1;

    /* ap_rep length, big-endian */

    *ptr++ = (ap_rep.length>>8) & 0xff;
    *ptr++ = ap_rep.length & 0xff;

    /* ap-rep data */

    if (ap_rep.length) {
        memcpy(ptr, ap_rep.data, ap_rep.length);
        ptr += ap_rep.length;
    }

    /* krb-priv or krb-error */

    memcpy(ptr, cipher.data, cipher.length);

bailout:
    krb5_auth_con_free(context, auth_context);
    krb5_free_principal(context, changepw);
    krb5_free_ticket(context, ticket);
    free(ap_rep.data);
    free(clear.data);
    free(cipher.data);
    krb5_free_principal(context, target);
    krb5_free_unparsed_name(context, targetstr);
    krb5_free_unparsed_name(context, clientstr);
    krb5_free_error_message(context, errmsg);
    return ret;
}
Пример #13
0
krb5_error_code 
krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_data *packet, int *result_code, krb5_data *result_data)
{
    char *ptr;
    int plen, vno;
    krb5_data ap_rep;
    krb5_ap_rep_enc_part *ap_rep_enc;
    krb5_error_code ret;
    krb5_data cipherresult;
    krb5_data clearresult;
    krb5_error *krberror;
    krb5_replay_data replay;
    krb5_keyblock *tmp;

    if (packet->length < 4)
	/* either this, or the server is printing bad messages,
	   or the caller passed in garbage */
	return(KRB5KRB_AP_ERR_MODIFIED);

    ptr = packet->data;

    /* verify length */

    plen = (*ptr++ & 0xff);
    plen = (plen<<8) | (*ptr++ & 0xff);

    if (plen != packet->length) 
	{
		/*
		 * MS KDCs *may* send back a KRB_ERROR.  Although
		 * not 100% correct via RFC3244, it's something
		 * we can workaround here.
		 */
		if (krb5_is_krb_error(packet)) {

			if ((ret = krb5_rd_error(context, packet, &krberror)))
			return(ret);

			if (krberror->e_data.data  == NULL) {
				ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
				krb5_free_error(context, krberror);
				return (ret);
			}
		}
		else
		{
			return(KRB5KRB_AP_ERR_MODIFIED);
		}
	}
	

    /* verify version number */

    vno = (*ptr++ & 0xff);
    vno = (vno<<8) | (*ptr++ & 0xff);

    if (vno != 1)
	return(KRB5KDC_ERR_BAD_PVNO);

    /* read, check ap-rep length */

    ap_rep.length = (*ptr++ & 0xff);
    ap_rep.length = (ap_rep.length<<8) | (*ptr++ & 0xff);

    if (ptr + ap_rep.length >= packet->data + packet->length)
	return(KRB5KRB_AP_ERR_MODIFIED);

    if (ap_rep.length) {
	/* verify ap_rep */
	ap_rep.data = ptr;
	ptr += ap_rep.length;

	/*
	 * Save send_subkey to later smash recv_subkey.
	 */
	ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmp);
	if (ret)
	    return ret;

	ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
	if (ret) {
	    krb5_free_keyblock(context, tmp);
	    return(ret);
	}

	krb5_free_ap_rep_enc_part(context, ap_rep_enc);

	/* extract and decrypt the result */

	cipherresult.data = ptr;
	cipherresult.length = (packet->data + packet->length) - ptr;

	/*
	 * Smash recv_subkey to be send_subkey, per spec.
	 */
	ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmp);
	krb5_free_keyblock(context, tmp);
	if (ret)
	    return ret;

	ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
			   &replay);

	if (ret)
	    return(ret);
    } else {
	cipherresult.data = ptr;
	cipherresult.length = (packet->data + packet->length) - ptr;

	if ((ret = krb5_rd_error(context, &cipherresult, &krberror)))
	    return(ret);

	clearresult = krberror->e_data;
    }

    if (clearresult.length < 2) {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	goto cleanup;
    }

    ptr = clearresult.data;

    *result_code = (*ptr++ & 0xff);
    *result_code = (*result_code<<8) | (*ptr++ & 0xff);

    if ((*result_code < KRB5_KPASSWD_SUCCESS) ||
	(*result_code > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)) {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	goto cleanup;
    }

    /* all success replies should be authenticated/encrypted */

    if ((ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS)) {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	goto cleanup;
    }

    result_data->length = (clearresult.data + clearresult.length) - ptr;

    if (result_data->length) {
	result_data->data = (char *) malloc(result_data->length);
	if (result_data->data == NULL) {
	    ret = ENOMEM;
	    goto cleanup;
	}
	memcpy(result_data->data, ptr, result_data->length);
    } else {
	result_data->data = NULL;
    }

    ret = 0;

cleanup:
    if (ap_rep.length) {
	krb5_xfree(clearresult.data);
    } else {
	krb5_free_error(context, krberror);
    }

    return(ret);
}
Пример #14
0
krb5_error_code 
krb5int_rd_setpw_rep( krb5_context context, krb5_auth_context auth_context, krb5_data *packet,
     int *result_code, krb5_data *result_data )
{
    char *ptr;
    unsigned int message_length, version_number;
    krb5_data ap_rep;
    krb5_ap_rep_enc_part *ap_rep_enc;
    krb5_error_code ret;
    krb5_data cipherresult;
    krb5_data clearresult;
    krb5_keyblock *tmpkey;
/*
** validate the packet length -
*/
    if (packet->length < 4)
	return(KRB5KRB_AP_ERR_MODIFIED);

    ptr = packet->data;

/*
** see if it is an error
*/
    if (krb5_is_krb_error(packet)) {
	krb5_error *krberror;
	if ((ret = krb5_rd_error(context, packet, &krberror)))
	    return(ret);
	if (krberror->e_data.data  == NULL) {
	    ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
	    krb5_free_error(context, krberror);
	    return (ret);
	}
	clearresult = krberror->e_data;
	krberror->e_data.data  = NULL; /*So we can free it later*/
	krberror->e_data.length = 0;
	krb5_free_error(context, krberror);
		
    } else { /* Not an error*/

/*
** validate the message length -
** length is big endian 
*/
	message_length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
	ptr += 2;
/*
** make sure the message length and packet length agree -
*/
	if (message_length != packet->length)
	    return(KRB5KRB_AP_ERR_MODIFIED);
/*
** get the version number -
*/
	version_number = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
	ptr += 2;
/*
** make sure we support the version returned -
*/
/*
** set password version is 0xff80, change password version is 1
*/
	if (version_number != 1 && version_number != 0xff80)
	    return(KRB5KDC_ERR_BAD_PVNO);
/*
** now fill in ap_rep with the reply -
*/
/*
** get the reply length -
*/
	ap_rep.length = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
	ptr += 2;
/*
** validate ap_rep length agrees with the packet length -
*/
	if (ptr + ap_rep.length >= packet->data + packet->length)
	    return(KRB5KRB_AP_ERR_MODIFIED);
/*
** if data was returned, set the ap_rep ptr -
*/
	if( ap_rep.length ) {
	    ap_rep.data = ptr;
	    ptr += ap_rep.length;

	    /*
	     * Save send_subkey to later smash recv_subkey.
	     */
	    ret = krb5_auth_con_getsendsubkey(context, auth_context, &tmpkey);
	    if (ret)
		return ret;

	    ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
	    if (ret) {
		krb5_free_keyblock(context, tmpkey);
		return(ret);
	    }

	    krb5_free_ap_rep_enc_part(context, ap_rep_enc);
/*
** now decrypt the result -
*/
	    cipherresult.data = ptr;
	    cipherresult.length = (packet->data + packet->length) - ptr;

	    /*
	     * Smash recv_subkey to be send_subkey, per spec.
	     */
	    ret = krb5_auth_con_setrecvsubkey(context, auth_context, tmpkey);
	    krb5_free_keyblock(context, tmpkey);
	    if (ret)
		return ret;

	    ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
			       NULL);
	    if (ret)
		return(ret);
	} /*We got an ap_rep*/
	else
	    return (KRB5KRB_AP_ERR_MODIFIED);
    } /*Response instead of error*/

/*
** validate the cleartext length 
*/
    if (clearresult.length < 2) {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	goto cleanup;
    }
/*
** now decode the result -
*/
    ptr = clearresult.data;

    *result_code = (((ptr[0] << 8)&0xff) | (ptr[1]&0xff));
    ptr += 2;

/*
** result code 5 is access denied
*/
    if ((*result_code < KRB5_KPASSWD_SUCCESS) || (*result_code > 5))
    {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	goto cleanup;
    }
/*
** all success replies should be authenticated/encrypted
*/
    if( (ap_rep.length == 0) && (*result_code == KRB5_KPASSWD_SUCCESS) )
    {
	ret = KRB5KRB_AP_ERR_MODIFIED;
	goto cleanup;
    }

    if (result_data) {
	result_data->length = (clearresult.data + clearresult.length) - ptr;

	if (result_data->length)
	{
	    result_data->data = (char *) malloc(result_data->length);
	    if (result_data->data)
		memcpy(result_data->data, ptr, result_data->length);
	}
	else
	    result_data->data = NULL;
    }
    ret = 0;

 cleanup:
    krb5_free_data_contents(context, &clearresult);
    return(ret);
}
Пример #15
0
static krb5_error_code
process_chpw_request(krb5_context context, void *server_handle,
			char *realm, int s, krb5_keytab keytab,
			struct sockaddr_in *sin, krb5_data *req,
			krb5_data *rep)
{
	krb5_error_code ret;
	char *ptr;
	int plen, vno;
	krb5_address local_kaddr, remote_kaddr;
	int allocated_mem = 0;
	krb5_data ap_req, ap_rep;
	krb5_auth_context auth_context;
	krb5_principal changepw;
	krb5_ticket *ticket;
	krb5_data cipher, clear;
	struct sockaddr local_addr, remote_addr;
	int addrlen;
	krb5_replay_data replay;
	krb5_error krberror;
	int numresult;
	char strresult[1024];

	ret = 0;
	rep->length = 0;

	auth_context = NULL;
	changepw = NULL;
	ap_rep.length = 0;
	ap_rep.data = NULL;
	ticket = NULL;
	clear.length = 0;
	clear.data = NULL;
	cipher.length = 0;
	cipher.data = NULL;

	if (req->length < 4) {
		/*
		 * either this, or the server is printing bad messages,
		 * or the caller passed in garbage
		 */
		ret = KRB5KRB_AP_ERR_MODIFIED;
		numresult = KRB5_KPASSWD_MALFORMED;
		(void) strlcpy(strresult, "Request was truncated",
				sizeof (strresult));
		goto chpwfail;
	}

	ptr = req->data;

	/*
	 * Verify length
	 */
	plen = (*ptr++ & 0xff);
	plen = (plen<<8) | (*ptr++ & 0xff);

	if (plen != req->length)
		return (KRB5KRB_AP_ERR_MODIFIED);

	/*
	 * Verify version number
	 */
	vno = (*ptr++ & 0xff);
	vno = (vno<<8) | (*ptr++ & 0xff);

	if (vno != 1) {
		ret = KRB5KDC_ERR_BAD_PVNO;
		numresult = KRB5_KPASSWD_MALFORMED;
		(void) snprintf(strresult, sizeof (strresult),
		    "Request contained unknown protocol version number %d",
		    vno);
		goto chpwfail;
	}

	/*
	 * Read, check ap-req length
	 */
	ap_req.length = (*ptr++ & 0xff);
	ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff);

	if (ptr + ap_req.length >= req->data + req->length) {
		ret = KRB5KRB_AP_ERR_MODIFIED;
		numresult = KRB5_KPASSWD_MALFORMED;
		(void) strlcpy(strresult, "Request was truncated in AP-REQ",
					sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Verify ap_req
	 */
	ap_req.data = ptr;
	ptr += ap_req.length;

	if (ret = krb5_auth_con_init(context, &auth_context)) {
		krb5_klog_syslog(LOG_ERR,
				gettext("Change password request failed. "
					"Failed initializing auth context: %s"),
				error_message(ret));
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult, "Failed initializing auth context",
					sizeof (strresult));
		goto chpwfail;
	}

	if (ret = krb5_auth_con_setflags(context, auth_context,
					KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
		krb5_klog_syslog(LOG_ERR,
				gettext("Change password request failed. "
						"Failed setting auth "
					    "context flags: %s"),
				error_message(ret));
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult, "Failed initializing auth context",
					sizeof (strresult));
		goto chpwfail;
	}

	if (ret = krb5_build_principal(context, &changepw, strlen(realm), realm,
				    "kadmin", "changepw", NULL)) {
		krb5_klog_syslog(LOG_ERR,
			gettext("Change password request failed "
					"Failed to build kadmin/changepw "
					"principal: %s"),
			error_message(ret));
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult,
				"Failed building kadmin/changepw principal",
				sizeof (strresult));
		goto chpwfail;
	}

	ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab,
			NULL, &ticket);

	if (ret) {
		char kt_name[MAX_KEYTAB_NAME_LEN];
		if (krb5_kt_get_name(context, keytab,
				kt_name, sizeof (kt_name)))
			strncpy(kt_name, "default keytab", sizeof (kt_name));

		switch (ret) {
		case KRB5_KT_NOTFOUND:
		krb5_klog_syslog(LOG_ERR,
			gettext("Change password request failed because "
					"keytab entry \"kadmin/changepw\" "
					"is missing from \"%s\""),
			kt_name);
		break;
		case ENOENT:
		krb5_klog_syslog(LOG_ERR,
			gettext("Change password request failed because "
					"keytab file \"%s\" does not exist"),
			kt_name);
		break;
		default:
		krb5_klog_syslog(LOG_ERR,
			gettext("Change password request failed. "
				"Failed to parse Kerberos AP_REQ message: %s"),
			error_message(ret));
		}

		numresult = KRB5_KPASSWD_AUTHERROR;
		(void) strlcpy(strresult, "Failed reading application request",
					sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Set up address info
	 */
	addrlen = sizeof (local_addr);

	if (getsockname(s, &local_addr, &addrlen) < 0) {
		ret = errno;
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult,
				"Failed getting server internet address",
				sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Some brain-dead OS's don't return useful information from
	 * the getsockname call.  Namely, windows and solaris.
	 */
	if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) {
		local_kaddr.addrtype = ADDRTYPE_INET;
		local_kaddr.length = sizeof (((struct sockaddr_in *)
						&local_addr)->sin_addr);
		/* CSTYLED */
		local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&local_addr)->sin_addr);
	} else {
		krb5_address **addrs;

		krb5_os_localaddr(context, &addrs);

		local_kaddr.magic = addrs[0]->magic;
		local_kaddr.addrtype = addrs[0]->addrtype;
		local_kaddr.length = addrs[0]->length;
		if ((local_kaddr.contents = malloc(addrs[0]->length)) == 0) {
			ret = errno;
			numresult = KRB5_KPASSWD_HARDERROR;
			(void) strlcpy(strresult,
				"Malloc failed for local_kaddr",
				sizeof (strresult));
			goto chpwfail;
		}

		(void) memcpy(local_kaddr.contents, addrs[0]->contents,
				addrs[0]->length);
		allocated_mem++;

		krb5_free_addresses(context, addrs);
	}

	addrlen = sizeof (remote_addr);

	if (getpeername(s, &remote_addr, &addrlen) < 0) {
		ret = errno;
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult,
				"Failed getting client internet address",
				sizeof (strresult));
		goto chpwfail;
	}

	remote_kaddr.addrtype = ADDRTYPE_INET;
	remote_kaddr.length = sizeof (((struct sockaddr_in *)
					&remote_addr)->sin_addr);
	/* CSTYLED */
	remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&remote_addr)->sin_addr);
	remote_kaddr.addrtype = ADDRTYPE_INET;
	remote_kaddr.length = sizeof (sin->sin_addr);
	remote_kaddr.contents = (krb5_octet *) &sin->sin_addr;

	/*
	 * mk_priv requires that the local address be set.
	 * getsockname is used for this.  rd_priv requires that the
	 * remote address be set.  recvfrom is used for this.  If
	 * rd_priv is given a local address, and the message has the
	 * recipient addr in it, this will be checked.  However, there
	 * is simply no way to know ahead of time what address the
	 * message will be delivered *to*.  Therefore, it is important
	 * that either no recipient address is in the messages when
	 * mk_priv is called, or that no local address is passed to
	 * rd_priv.  Both is a better idea, and I have done that.  In
	 * summary, when mk_priv is called, *only* a local address is
	 * specified.  when rd_priv is called, *only* a remote address
	 * is specified.  Are we having fun yet?
	 */
	if (ret = krb5_auth_con_setaddrs(context, auth_context, NULL,
					&remote_kaddr)) {
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult,
				"Failed storing client internet address",
				sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Verify that this is an AS_REQ ticket
	 */
	if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) {
		numresult = KRB5_KPASSWD_AUTHERROR;
		(void) strlcpy(strresult,
				"Ticket must be derived from a password",
				sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Construct the ap-rep
	 */
	if (ret = krb5_mk_rep(context, auth_context, &ap_rep)) {
		numresult = KRB5_KPASSWD_AUTHERROR;
		(void) strlcpy(strresult,
				"Failed replying to application request",
				sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Decrypt the new password
	 */
	cipher.length = (req->data + req->length) - ptr;
	cipher.data = ptr;

	if (ret = krb5_rd_priv(context, auth_context, &cipher,
				&clear, &replay)) {
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult, "Failed decrypting request",
					sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Change the password
	 */
	if ((ptr = (char *)malloc(clear.length + 1)) == NULL) {
		ret = errno;
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult, "Malloc failed for ptr",
			sizeof (strresult));
		goto chpwfail;
	}
	(void) memcpy(ptr, clear.data, clear.length);
	ptr[clear.length] = '\0';

	ret = (kadm5_ret_t)kadm5_chpass_principal_util(server_handle,
						ticket->enc_part2->client,
						ptr, NULL, strresult,
						sizeof (strresult));
	/*
	 * Zap the password
	 */
	(void) memset(clear.data, 0, clear.length);
	(void) memset(ptr, 0, clear.length);
	if (clear.data != NULL) {
		krb5_xfree(clear.data);
		clear.data = NULL;
	}
	free(ptr);
	clear.length = 0;

	if (ret) {
		if ((ret != KADM5_PASS_Q_TOOSHORT) &&
		    (ret != KADM5_PASS_REUSE) &&
		    (ret != KADM5_PASS_Q_CLASS) &&
		    (ret != KADM5_PASS_Q_DICT) &&
		    (ret != KADM5_PASS_TOOSOON))
			numresult = KRB5_KPASSWD_HARDERROR;
		else
			numresult = KRB5_KPASSWD_SOFTERROR;
		/*
		 * strresult set by kadb5_chpass_principal_util()
		 */
		goto chpwfail;
	}

	/*
	 * Success!
	 */
	numresult = KRB5_KPASSWD_SUCCESS;
	(void) strlcpy(strresult, "", sizeof (strresult));

chpwfail:

	clear.length = 2 + strlen(strresult);
	if (clear.data != NULL) {
		krb5_xfree(clear.data);
		clear.data = NULL;
	}
	if ((clear.data = (char *)malloc(clear.length)) == NULL) {
		ret = errno;
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult, "Malloc failed for clear.data",
			sizeof (strresult));
	}

	cipher.length = 0;

	if (ap_rep.length) {
		if (ret = krb5_auth_con_setaddrs(context, auth_context,
					&local_kaddr, NULL)) {
		    numresult = KRB5_KPASSWD_HARDERROR;
		    (void) strlcpy(strresult,
			"Failed storing client and server internet addresses",
			sizeof (strresult));
		} else {
			if (ret = krb5_mk_priv(context, auth_context, &clear,
						&cipher, &replay)) {
				numresult = KRB5_KPASSWD_HARDERROR;
				(void) strlcpy(strresult,
					"Failed encrypting reply",
					sizeof (strresult));
			}
		}
	}

	ptr = clear.data;
	*ptr++ = (numresult>>8) & 0xff;
	*ptr++ = numresult & 0xff;

	(void) memcpy(ptr, strresult, strlen(strresult));

	/*
	 * If no KRB-PRIV was constructed, then we need a KRB-ERROR.
	 * If this fails, just bail.  There's nothing else we can do.
	 */
	if (cipher.length == 0) {
		/*
		 * Clear out ap_rep now, so that it won't be inserted
		 * in the reply
		 */
		if (ap_rep.length) {
			if (ap_rep.data != NULL)
				krb5_xfree(ap_rep.data);
			ap_rep.data = NULL;
			ap_rep.length = 0;
		}

		krberror.ctime = 0;
		krberror.cusec = 0;
		krberror.susec = 0;
		if (ret = krb5_timeofday(context, &krberror.stime))
			goto bailout;

		/*
		 * This is really icky.  but it's what all the other callers
		 * to mk_error do.
		 */
		krberror.error = ret;
		krberror.error -= ERROR_TABLE_BASE_krb5;
		if (krberror.error < 0 || krberror.error > 128)
			krberror.error = KRB_ERR_GENERIC;

		krberror.client = NULL;
		if (ret = krb5_build_principal(context, &krberror.server,
					    strlen(realm), realm,
					    "kadmin", "changepw", NULL)) {
			goto bailout;
		}

		krberror.text.length = 0;
		krberror.e_data = clear;

		ret = krb5_mk_error(context, &krberror, &cipher);

		krb5_free_principal(context, krberror.server);

		if (ret)
			goto bailout;
	}

	/*
	 * Construct the reply
	 */
	rep->length = 6 + ap_rep.length + cipher.length;
	if ((rep->data = (char *)malloc(rep->length)) == NULL)  {
		ret = errno;
		goto bailout;
	}
	ptr = rep->data;

	/*
	 * Length
	 */
	*ptr++ = (rep->length>>8) & 0xff;
	*ptr++ = rep->length & 0xff;

	/*
	 * Version == 0x0001 big-endian
	 */
	*ptr++ = 0;
	*ptr++ = 1;

	/*
	 * ap_rep length, big-endian
	 */
	*ptr++ = (ap_rep.length>>8) & 0xff;
	*ptr++ = ap_rep.length & 0xff;

	/*
	 * ap-rep data
	 */
	if (ap_rep.length) {
		(void) memcpy(ptr, ap_rep.data, ap_rep.length);
		ptr += ap_rep.length;
	}

	/*
	 * krb-priv or krb-error
	 */
	(void) memcpy(ptr, cipher.data, cipher.length);

bailout:
	if (auth_context)
		krb5_auth_con_free(context, auth_context);
	if (changepw)
		krb5_free_principal(context, changepw);
	if (ap_rep.data != NULL)
		krb5_xfree(ap_rep.data);
	if (ticket)
		krb5_free_ticket(context, ticket);
	if (clear.data != NULL)
		krb5_xfree(clear.data);
	if (cipher.data != NULL)
		krb5_xfree(cipher.data);
	if (allocated_mem)
		krb5_xfree(local_kaddr.contents);

	return (ret);
}