Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 2629971b9802f2e8b84eab7c2c1854e354b65eca (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*******************************************************************************
 * Copyright (c) 2006, 2013 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.osgi.internal.signedcontent;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.*;
import java.security.cert.*;
import java.util.Date;
import org.eclipse.osgi.signedcontent.*;
import org.eclipse.osgi.storage.bundlefile.*;
import org.eclipse.osgi.util.NLS;

/**
 * This class wraps a Repository of classes and resources to check and enforce
 * signatures. It requires full signing of the manifest by all signers. If no
 * signatures are found, the classes and resources are retrieved without checks.
 */
public class SignedBundleFile extends BundleFileWrapper implements SignedContentConstants, SignedContent {
	SignedContentImpl signedContent;
	private final int supportFlags;
	private final SignedBundleHook signedBundleHook;

	SignedBundleFile(BundleFile bundleFile, SignedContentImpl signedContent, int supportFlags, SignedBundleHook signedBundleHook) {
		super(bundleFile);
		this.signedContent = signedContent;
		this.supportFlags = supportFlags;
		this.signedBundleHook = signedBundleHook;
	}

	void initializeSignedContent() throws IOException, InvalidKeyException, SignatureException, CertificateException, NoSuchAlgorithmException, NoSuchProviderException {
		if (signedContent == null) {
			SignatureBlockProcessor signatureProcessor = new SignatureBlockProcessor(this, supportFlags, signedBundleHook);
			signedContent = signatureProcessor.process();
			if (signedContent != null)
				signedBundleHook.determineTrust(signedContent, supportFlags);
		}
	}

	public BundleEntry getEntry(String path) {
		// strip off leading slashes so we can ensure the path matches the one provided in the manifest.
		if (path.length() > 0 && path.charAt(0) == '/')
			path = path.substring(1);
		BundleEntry be = getBundleFile().getEntry(path);
		if ((supportFlags & SignedBundleHook.VERIFY_RUNTIME) == 0 || signedContent == null)
			return be;
		if (path.startsWith(META_INF)) {
			int lastSlash = path.lastIndexOf('/');
			if (lastSlash == META_INF.length() - 1) {
				if (path.equals(META_INF_MANIFEST_MF) || path.endsWith(DOT_DSA) || path.endsWith(DOT_RSA) || path.endsWith(DOT_SF) || path.indexOf(SIG_DASH) == META_INF.length())
					return be;
				SignedContentEntry signedEntry = signedContent.getSignedEntry(path);
				if (signedEntry == null)
					// TODO this is to allow 1.4 signed bundles to work, it would be better if we could detect 1.4 signed bundles and only do this for them.
					return be;
			}
		}
		if (be == null) {
			// double check that no signer thinks it should exist
			SignedContentEntry signedEntry = signedContent.getSignedEntry(path);
			if (signedEntry != null)
				throw new SecurityException(NLS.bind(SignedContentMessages.file_is_removed_from_jar, path, getBaseFile().toString()));
			return null;
		}
		return new SignedBundleEntry(be);
	}

	class SignedBundleEntry extends BundleEntry {
		BundleEntry nestedEntry;

		SignedBundleEntry(BundleEntry nestedEntry) {
			this.nestedEntry = nestedEntry;
		}

		public InputStream getInputStream() throws IOException {
			InputStream in = signedContent.getDigestInputStream(nestedEntry);
			if (in == null)
				throw new SecurityException("Corrupted file: the digest does not exist for the file " + nestedEntry.getName()); //$NON-NLS-1$
			return in;
		}

		public long getSize() {
			return nestedEntry.getSize();
		}

		public String getName() {
			return nestedEntry.getName();
		}

		public long getTime() {
			return nestedEntry.getTime();
		}

		public URL getLocalURL() {
			return nestedEntry.getLocalURL();
		}

		public URL getFileURL() {
			return nestedEntry.getFileURL();
		}

	}

	SignedContentImpl getSignedContent() {
		return signedContent;
	}

	public SignedContentEntry[] getSignedEntries() {
		return signedContent == null ? null : signedContent.getSignedEntries();
	}

	public SignedContentEntry getSignedEntry(String name) {
		return signedContent == null ? null : signedContent.getSignedEntry(name);
	}

	public SignerInfo[] getSignerInfos() {
		return signedContent == null ? null : signedContent.getSignerInfos();
	}

	public Date getSigningTime(SignerInfo signerInfo) {
		return signedContent == null ? null : signedContent.getSigningTime(signerInfo);
	}

	public SignerInfo getTSASignerInfo(SignerInfo signerInfo) {
		return signedContent == null ? null : signedContent.getTSASignerInfo(signerInfo);
	}

	public boolean isSigned() {
		return signedContent == null ? false : signedContent.isSigned();
	}

	public void checkValidity(SignerInfo signerInfo) throws CertificateExpiredException, CertificateNotYetValidException {
		if (signedContent != null)
			signedContent.checkValidity(signerInfo);
	}

}

Back to the top