Skip to content

Commit

Permalink
sync up with jcifs-ng
Browse files Browse the repository at this point in the history
  • Loading branch information
marevol committed Oct 2, 2023
2 parents 15b58b3 + 0eb193e commit 814a8ec
Show file tree
Hide file tree
Showing 10 changed files with 305 additions and 96 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.74</version>
<version>1.76</version>
</dependency>
<dependency>
<groupId>junit</groupId>
Expand Down
150 changes: 143 additions & 7 deletions src/main/java/jcifs/pac/ASN1Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@
package jcifs.pac;


import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.*;


/**
Expand Down Expand Up @@ -77,16 +76,27 @@ public static <T extends ASN1Primitive> T as ( Class<T> type, ASN1InputStream st


/**
*
*
* @param type
* @param tagged
* @return tagged object contents cast to type
* @throws PACDecodingException
*/
public static <T extends ASN1Primitive> T as ( Class<T> type, ASN1TaggedObject tagged ) throws PACDecodingException {
return as(type, tagged.getObject());
return as(type, tagged.getBaseObject());
}

/**
*
* @param type
* @param sequence
* @param index
* @return sequence element cast to type
* @throws PACDecodingException
*/
public static <T extends ASN1Primitive> T as ( Class<T> type, DLSequence sequence, int index ) throws PACDecodingException {
return as(type, sequence, index);
}

/**
*
Expand All @@ -96,8 +106,134 @@ public static <T extends ASN1Primitive> T as ( Class<T> type, ASN1TaggedObject t
* @return sequence element cast to type
* @throws PACDecodingException
*/
public static <T extends ASN1Primitive> T as ( Class<T> type, DLSequence sequence, int index ) throws PACDecodingException {
public static <T extends ASN1Primitive> T as ( Class<T> type, ASN1Sequence sequence, int index ) throws PACDecodingException {
return as(type, sequence.getObjectAt(index));
}


/**
* Read a tagged object without parsing it's contents
*
* BC no longer seems to allow that out of the box
*
* @param expectTag
* @param in
* @return coded bytes of the tagged object
* @throws IOException
*/
public static byte[] readUnparsedTagged(int expectTag, int limit, ASN1InputStream in) throws IOException {
int ftag = in.read();
int tag = readTagNumber(in, ftag);
if ( tag != expectTag ) {
throw new IOException("Unexpected tag " + tag);
}
int length = readLength(in, limit, false);
byte[] content = new byte[length];
in.read(content);
return content;
}

// shamelessly stolen from BC ASN1InputStream
static int readTagNumber(InputStream s, int tag)
throws IOException
{
int tagNo = tag & 0x1f;

//
// with tagged object tag number is bottom 5 bits, or stored at the start of the content
//
if (tagNo == 0x1f)
{
int b = s.read();
if (b < 31)
{
if (b < 0)
{
throw new EOFException("EOF found inside tag value.");
}
throw new IOException("corrupted stream - high tag number < 31 found");
}

tagNo = b & 0x7f;

// X.690-0207 8.1.2.4.2
// "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
if (0 == tagNo)
{
throw new IOException("corrupted stream - invalid high tag number found");
}

while ((b & 0x80) != 0)
{
if ((tagNo >>> 24) != 0)
{
throw new IOException("Tag number more than 31 bits");
}

tagNo <<= 7;

b = s.read();
if (b < 0)
{
throw new EOFException("EOF found inside tag value.");
}

tagNo |= (b & 0x7f);
}
}

return tagNo;
}

static int readLength(InputStream s, int limit, boolean isParsing)
throws IOException
{
int length = s.read();
if (0 == (length >>> 7))
{
// definite-length short form
return length;
}
if (0x80 == length)
{
// indefinite-length
return -1;
}
if (length < 0)
{
throw new EOFException("EOF found when length expected");
}
if (0xFF == length)
{
throw new IOException("invalid long form definite-length 0xFF");
}

int octetsCount = length & 0x7F, octetsPos = 0;

length = 0;
do
{
int octet = s.read();
if (octet < 0)
{
throw new EOFException("EOF found reading length");
}

if ((length >>> 23) != 0)
{
throw new IOException("long form definite-length more than 31 bits");
}

length = (length << 8) + octet;
}
while (++octetsPos < octetsCount);

if (length >= limit && !isParsing) // after all we must have read at least 1 byte
{
throw new IOException("corrupted stream - out of bounds length found: " + length + " >= " + limit);
}

return length;
}

}
30 changes: 18 additions & 12 deletions src/main/java/jcifs/pac/kerberos/KerberosApRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,7 @@

import javax.security.auth.kerberos.KerberosKey;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERApplicationSpecific;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.*;

import jcifs.pac.ASN1Util;
import jcifs.pac.PACDecodingException;
Expand All @@ -43,20 +38,27 @@ public class KerberosApRequest {


public KerberosApRequest ( byte[] token, KerberosKey[] keys ) throws PACDecodingException {
this(parseSequence(token), keys);
}

private static ASN1Sequence parseSequence(byte[] token) throws PACDecodingException {
if ( token.length <= 0 )
throw new PACDecodingException("Empty kerberos ApReq");

DLSequence sequence;
ASN1Sequence sequence;
try {
try ( ASN1InputStream stream = new ASN1InputStream(new ByteArrayInputStream(token)) ) {
sequence = ASN1Util.as(DLSequence.class, stream);
sequence = ASN1Util.as(ASN1Sequence.class, stream);
}
}
catch ( IOException e ) {
throw new PACDecodingException("Malformed Kerberos Ticket", e);
}
return sequence;
}

Enumeration<?> fields = sequence.getObjects();
public KerberosApRequest(ASN1Sequence seq, KerberosKey[] keys) throws PACDecodingException {
Enumeration<?> fields = seq.getObjects();
while ( fields.hasMoreElements() ) {
ASN1TaggedObject tagged = ASN1Util.as(ASN1TaggedObject.class, fields.nextElement());
switch ( tagged.getTagNo() ) {
Expand All @@ -76,10 +78,14 @@ public KerberosApRequest ( byte[] token, KerberosKey[] keys ) throws PACDecoding
this.apOptions = bitString.getBytes()[ 0 ];
break;
case 3:
DERApplicationSpecific derTicket = ASN1Util.as(DERApplicationSpecific.class, tagged);
if ( !derTicket.isConstructed() )
ASN1TaggedObject derTicket = ASN1Util.as(ASN1TaggedObject.class, tagged);
if ( derTicket.getTagClass() != BERTags.APPLICATION )
throw new PACDecodingException("Malformed Kerberos Ticket");
this.ticket = new KerberosTicket(derTicket.getContents(), this.apOptions, keys);
try {
this.ticket = new KerberosTicket(derTicket.getBaseObject().getEncoded(), this.apOptions, keys);
} catch (IOException e) {
throw new PACDecodingException("Malformed Kerberos Ticket", e);
}
break;
case 4:
// Let's ignore this for now
Expand Down
1 change: 1 addition & 0 deletions src/main/java/jcifs/pac/kerberos/KerberosCredentials.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.kerberos.KeyTab;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

Expand Down
37 changes: 14 additions & 23 deletions src/main/java/jcifs/pac/kerberos/KerberosEncData.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,7 @@
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.kerberos.KerberosKey;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERApplicationSpecific;
import org.bouncycastle.asn1.DERGeneralString;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.*;

import jcifs.pac.ASN1Util;
import jcifs.pac.PACDecodingException;
Expand All @@ -66,22 +59,20 @@ public class KerberosEncData {

public KerberosEncData ( byte[] token, Map<Integer, KerberosKey> keys ) throws PACDecodingException {
ASN1InputStream stream = new ASN1InputStream(new ByteArrayInputStream(token));
DERApplicationSpecific derToken;
ASN1TaggedObject derToken;
try {
derToken = ASN1Util.as(DERApplicationSpecific.class, stream);
if ( !derToken.isConstructed() )
derToken = ASN1Util.as(ASN1TaggedObject.class, stream);
if ( derToken.getTagClass() != BERTags.APPLICATION )
throw new PACDecodingException("Malformed kerberos ticket");
stream.close();
}
catch ( IOException e ) {
throw new PACDecodingException("Malformed kerberos ticket", e);
}

stream = new ASN1InputStream(new ByteArrayInputStream(derToken.getContents()));
DLSequence sequence;
ASN1Sequence sequence;
try {
sequence = ASN1Util.as(DLSequence.class, stream);
stream.close();
sequence = ASN1Util.as(ASN1Sequence.class, derToken.getBaseObject());
}
catch ( IOException e ) {
throw new PACDecodingException("Malformed kerberos ticket", e);
Expand All @@ -101,8 +92,8 @@ public KerberosEncData ( byte[] token, Map<Integer, KerberosKey> keys ) throws P
this.userRealm = derRealm.getString();
break;
case 3: // Principal
DLSequence principalSequence = ASN1Util.as(DLSequence.class, tagged);
DLSequence nameSequence = ASN1Util.as(DLSequence.class, ASN1Util.as(DERTaggedObject.class, principalSequence, 1));
ASN1Sequence principalSequence = ASN1Util.as(ASN1Sequence.class, tagged);
ASN1Sequence nameSequence = ASN1Util.as(ASN1Sequence.class, ASN1Util.as(ASN1TaggedObject.class, principalSequence, 1));

StringBuilder nameBuilder = new StringBuilder();
Enumeration<?> parts = nameSequence.getObjects();
Expand Down Expand Up @@ -134,10 +125,10 @@ public KerberosEncData ( byte[] token, Map<Integer, KerberosKey> keys ) throws P
// DERGeneralizedTime.class);
break;
case 9: // Host Addresses
DLSequence adressesSequence = ASN1Util.as(DLSequence.class, tagged);
ASN1Sequence adressesSequence = ASN1Util.as(ASN1Sequence.class, tagged);
Enumeration<?> adresses = adressesSequence.getObjects();
while ( adresses.hasMoreElements() ) {
DLSequence addressSequence = ASN1Util.as(DLSequence.class, adresses);
ASN1Sequence addressSequence = ASN1Util.as(ASN1Sequence.class, adresses);
ASN1Integer addressType = ASN1Util.as(ASN1Integer.class, addressSequence, 0);
DEROctetString addressOctets = ASN1Util.as(DEROctetString.class, addressSequence, 1);

Expand All @@ -153,14 +144,14 @@ public KerberosEncData ( byte[] token, Map<Integer, KerberosKey> keys ) throws P
}
break;
case 10: // Authorization Data
DLSequence authSequence = ASN1Util.as(DLSequence.class, tagged);
ASN1Sequence authSequence = ASN1Util.as(ASN1Sequence.class, tagged);

this.userAuthorizations = new ArrayList<>();
Enumeration<?> authElements = authSequence.getObjects();
while ( authElements.hasMoreElements() ) {
DLSequence authElement = ASN1Util.as(DLSequence.class, authElements);
ASN1Integer authType = ASN1Util.as(ASN1Integer.class, ASN1Util.as(DERTaggedObject.class, authElement, 0));
DEROctetString authData = ASN1Util.as(DEROctetString.class, ASN1Util.as(DERTaggedObject.class, authElement, 1));
ASN1Sequence authElement = ASN1Util.as(ASN1Sequence.class, authElements);
ASN1Integer authType = ASN1Util.as(ASN1Integer.class, ASN1Util.as(ASN1TaggedObject.class, authElement, 0));
DEROctetString authData = ASN1Util.as(DEROctetString.class, ASN1Util.as(ASN1TaggedObject.class, authElement, 1));

this.userAuthorizations.addAll(KerberosAuthData.parse(authType.getValue().intValue(), authData.getOctets(), keys));
}
Expand Down
16 changes: 6 additions & 10 deletions src/main/java/jcifs/pac/kerberos/KerberosRelevantAuthData.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@

import javax.security.auth.kerberos.KerberosKey;

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.*;

import jcifs.pac.ASN1Util;
import jcifs.pac.PACDecodingException;
Expand All @@ -43,10 +39,10 @@ public class KerberosRelevantAuthData extends KerberosAuthData {


public KerberosRelevantAuthData ( byte[] token, Map<Integer, KerberosKey> keys ) throws PACDecodingException {
DLSequence authSequence;
ASN1Sequence authSequence;
try {
try ( ASN1InputStream stream = new ASN1InputStream(new ByteArrayInputStream(token)) ) {
authSequence = ASN1Util.as(DLSequence.class, stream);
authSequence = ASN1Util.as(ASN1Sequence.class, stream);
}
}
catch ( IOException e ) {
Expand All @@ -56,9 +52,9 @@ public KerberosRelevantAuthData ( byte[] token, Map<Integer, KerberosKey> keys )
this.authorizations = new ArrayList<>();
Enumeration<?> authElements = authSequence.getObjects();
while ( authElements.hasMoreElements() ) {
DLSequence authElement = ASN1Util.as(DLSequence.class, authElements);
ASN1Integer authType = ASN1Util.as(ASN1Integer.class, ASN1Util.as(DERTaggedObject.class, authElement, 0));
DEROctetString authData = ASN1Util.as(DEROctetString.class, ASN1Util.as(DERTaggedObject.class, authElement, 1));
ASN1Sequence authElement = ASN1Util.as(ASN1Sequence.class, authElements);
ASN1Integer authType = ASN1Util.as(ASN1Integer.class, ASN1Util.as(ASN1TaggedObject.class, authElement, 0));
DEROctetString authData = ASN1Util.as(DEROctetString.class, ASN1Util.as(ASN1TaggedObject.class, authElement, 1));

this.authorizations.addAll(KerberosAuthData.parse(authType.getValue().intValue(), authData.getOctets(), keys));
}
Expand Down
Loading

0 comments on commit 814a8ec

Please sign in to comment.