1189251Ssam/*
2189251Ssam * TLSv1 credentials
3214734Srpaulo * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12189251Ssam#include "base64.h"
13214734Srpaulo#include "crypto/crypto.h"
14189251Ssam#include "x509v3.h"
15189251Ssam#include "tlsv1_cred.h"
16189251Ssam
17189251Ssam
18189251Ssamstruct tlsv1_credentials * tlsv1_cred_alloc(void)
19189251Ssam{
20189251Ssam	struct tlsv1_credentials *cred;
21189251Ssam	cred = os_zalloc(sizeof(*cred));
22189251Ssam	return cred;
23189251Ssam}
24189251Ssam
25189251Ssam
26189251Ssamvoid tlsv1_cred_free(struct tlsv1_credentials *cred)
27189251Ssam{
28189251Ssam	if (cred == NULL)
29189251Ssam		return;
30189251Ssam
31189251Ssam	x509_certificate_chain_free(cred->trusted_certs);
32189251Ssam	x509_certificate_chain_free(cred->cert);
33189251Ssam	crypto_private_key_free(cred->key);
34189251Ssam	os_free(cred->dh_p);
35189251Ssam	os_free(cred->dh_g);
36189251Ssam	os_free(cred);
37189251Ssam}
38189251Ssam
39189251Ssam
40189251Ssamstatic int tlsv1_add_cert_der(struct x509_certificate **chain,
41189251Ssam			      const u8 *buf, size_t len)
42189251Ssam{
43252726Srpaulo	struct x509_certificate *cert, *p;
44189251Ssam	char name[128];
45189251Ssam
46189251Ssam	cert = x509_certificate_parse(buf, len);
47189251Ssam	if (cert == NULL) {
48189251Ssam		wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate",
49189251Ssam			   __func__);
50189251Ssam		return -1;
51189251Ssam	}
52189251Ssam
53252726Srpaulo	p = *chain;
54252726Srpaulo	while (p && p->next)
55252726Srpaulo		p = p->next;
56252726Srpaulo	if (p && x509_name_compare(&cert->subject, &p->issuer) == 0) {
57252726Srpaulo		/*
58252726Srpaulo		 * The new certificate is the issuer of the last certificate in
59252726Srpaulo		 * the chain - add the new certificate to the end.
60252726Srpaulo		 */
61252726Srpaulo		p->next = cert;
62252726Srpaulo	} else {
63252726Srpaulo		/* Add to the beginning of the chain */
64252726Srpaulo		cert->next = *chain;
65252726Srpaulo		*chain = cert;
66252726Srpaulo	}
67189251Ssam
68189251Ssam	x509_name_string(&cert->subject, name, sizeof(name));
69189251Ssam	wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
70189251Ssam
71189251Ssam	return 0;
72189251Ssam}
73189251Ssam
74189251Ssam
75189251Ssamstatic const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
76189251Ssamstatic const char *pem_cert_end = "-----END CERTIFICATE-----";
77214734Srpaulostatic const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
78214734Srpaulostatic const char *pem_key_end = "-----END RSA PRIVATE KEY-----";
79214734Srpaulostatic const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----";
80214734Srpaulostatic const char *pem_key2_end = "-----END PRIVATE KEY-----";
81214734Srpaulostatic const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
82214734Srpaulostatic const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----";
83189251Ssam
84189251Ssam
85189251Ssamstatic const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
86189251Ssam{
87189251Ssam	size_t i, plen;
88189251Ssam
89189251Ssam	plen = os_strlen(tag);
90189251Ssam	if (len < plen)
91189251Ssam		return NULL;
92189251Ssam
93189251Ssam	for (i = 0; i < len - plen; i++) {
94189251Ssam		if (os_memcmp(buf + i, tag, plen) == 0)
95189251Ssam			return buf + i;
96189251Ssam	}
97189251Ssam
98189251Ssam	return NULL;
99189251Ssam}
100189251Ssam
101189251Ssam
102189251Ssamstatic int tlsv1_add_cert(struct x509_certificate **chain,
103189251Ssam			  const u8 *buf, size_t len)
104189251Ssam{
105189251Ssam	const u8 *pos, *end;
106189251Ssam	unsigned char *der;
107189251Ssam	size_t der_len;
108189251Ssam
109189251Ssam	pos = search_tag(pem_cert_begin, buf, len);
110189251Ssam	if (!pos) {
111189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
112189251Ssam			   "assume DER format");
113189251Ssam		return tlsv1_add_cert_der(chain, buf, len);
114189251Ssam	}
115189251Ssam
116189251Ssam	wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
117189251Ssam		   "DER format");
118189251Ssam
119189251Ssam	while (pos) {
120189251Ssam		pos += os_strlen(pem_cert_begin);
121189251Ssam		end = search_tag(pem_cert_end, pos, buf + len - pos);
122189251Ssam		if (end == NULL) {
123189251Ssam			wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
124189251Ssam				   "certificate end tag (%s)", pem_cert_end);
125189251Ssam			return -1;
126189251Ssam		}
127189251Ssam
128189251Ssam		der = base64_decode(pos, end - pos, &der_len);
129189251Ssam		if (der == NULL) {
130189251Ssam			wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
131189251Ssam				   "certificate");
132189251Ssam			return -1;
133189251Ssam		}
134189251Ssam
135189251Ssam		if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
136189251Ssam			wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
137189251Ssam				   "certificate after DER conversion");
138189251Ssam			os_free(der);
139189251Ssam			return -1;
140189251Ssam		}
141189251Ssam
142189251Ssam		os_free(der);
143189251Ssam
144189251Ssam		end += os_strlen(pem_cert_end);
145189251Ssam		pos = search_tag(pem_cert_begin, end, buf + len - end);
146189251Ssam	}
147189251Ssam
148189251Ssam	return 0;
149189251Ssam}
150189251Ssam
151189251Ssam
152189251Ssamstatic int tlsv1_set_cert_chain(struct x509_certificate **chain,
153189251Ssam				const char *cert, const u8 *cert_blob,
154189251Ssam				size_t cert_blob_len)
155189251Ssam{
156189251Ssam	if (cert_blob)
157189251Ssam		return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
158189251Ssam
159189251Ssam	if (cert) {
160189251Ssam		u8 *buf;
161189251Ssam		size_t len;
162189251Ssam		int ret;
163189251Ssam
164189251Ssam		buf = (u8 *) os_readfile(cert, &len);
165189251Ssam		if (buf == NULL) {
166189251Ssam			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
167189251Ssam				   cert);
168189251Ssam			return -1;
169189251Ssam		}
170189251Ssam
171189251Ssam		ret = tlsv1_add_cert(chain, buf, len);
172189251Ssam		os_free(buf);
173189251Ssam		return ret;
174189251Ssam	}
175189251Ssam
176189251Ssam	return 0;
177189251Ssam}
178189251Ssam
179189251Ssam
180189251Ssam/**
181189251Ssam * tlsv1_set_ca_cert - Set trusted CA certificate(s)
182189251Ssam * @cred: TLSv1 credentials from tlsv1_cred_alloc()
183189251Ssam * @cert: File or reference name for X.509 certificate in PEM or DER format
184189251Ssam * @cert_blob: cert as inlined data or %NULL if not used
185189251Ssam * @cert_blob_len: ca_cert_blob length
186189251Ssam * @path: Path to CA certificates (not yet supported)
187189251Ssam * Returns: 0 on success, -1 on failure
188189251Ssam */
189189251Ssamint tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
190189251Ssam		      const u8 *cert_blob, size_t cert_blob_len,
191189251Ssam		      const char *path)
192189251Ssam{
193189251Ssam	if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
194189251Ssam				 cert_blob, cert_blob_len) < 0)
195189251Ssam		return -1;
196189251Ssam
197189251Ssam	if (path) {
198189251Ssam		/* TODO: add support for reading number of certificate files */
199189251Ssam		wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
200189251Ssam			   "not yet supported");
201189251Ssam		return -1;
202189251Ssam	}
203189251Ssam
204189251Ssam	return 0;
205189251Ssam}
206189251Ssam
207189251Ssam
208189251Ssam/**
209189251Ssam * tlsv1_set_cert - Set certificate
210189251Ssam * @cred: TLSv1 credentials from tlsv1_cred_alloc()
211189251Ssam * @cert: File or reference name for X.509 certificate in PEM or DER format
212189251Ssam * @cert_blob: cert as inlined data or %NULL if not used
213189251Ssam * @cert_blob_len: cert_blob length
214189251Ssam * Returns: 0 on success, -1 on failure
215189251Ssam */
216189251Ssamint tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
217189251Ssam		   const u8 *cert_blob, size_t cert_blob_len)
218189251Ssam{
219189251Ssam	return tlsv1_set_cert_chain(&cred->cert, cert,
220189251Ssam				    cert_blob, cert_blob_len);
221189251Ssam}
222189251Ssam
223189251Ssam
224214734Srpaulostatic struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
225214734Srpaulo{
226214734Srpaulo	const u8 *pos, *end;
227214734Srpaulo	unsigned char *der;
228214734Srpaulo	size_t der_len;
229214734Srpaulo	struct crypto_private_key *pkey;
230214734Srpaulo
231214734Srpaulo	pos = search_tag(pem_key_begin, key, len);
232214734Srpaulo	if (!pos) {
233214734Srpaulo		pos = search_tag(pem_key2_begin, key, len);
234214734Srpaulo		if (!pos)
235214734Srpaulo			return NULL;
236214734Srpaulo		pos += os_strlen(pem_key2_begin);
237214734Srpaulo		end = search_tag(pem_key2_end, pos, key + len - pos);
238214734Srpaulo		if (!end)
239214734Srpaulo			return NULL;
240214734Srpaulo	} else {
241252726Srpaulo		const u8 *pos2;
242214734Srpaulo		pos += os_strlen(pem_key_begin);
243214734Srpaulo		end = search_tag(pem_key_end, pos, key + len - pos);
244214734Srpaulo		if (!end)
245214734Srpaulo			return NULL;
246252726Srpaulo		pos2 = search_tag("Proc-Type: 4,ENCRYPTED", pos, end - pos);
247252726Srpaulo		if (pos2) {
248252726Srpaulo			wpa_printf(MSG_DEBUG, "TLSv1: Unsupported private key "
249252726Srpaulo				   "format (Proc-Type/DEK-Info)");
250252726Srpaulo			return NULL;
251252726Srpaulo		}
252214734Srpaulo	}
253214734Srpaulo
254214734Srpaulo	der = base64_decode(pos, end - pos, &der_len);
255214734Srpaulo	if (!der)
256214734Srpaulo		return NULL;
257214734Srpaulo	pkey = crypto_private_key_import(der, der_len, NULL);
258214734Srpaulo	os_free(der);
259214734Srpaulo	return pkey;
260214734Srpaulo}
261214734Srpaulo
262214734Srpaulo
263214734Srpaulostatic struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key,
264214734Srpaulo							 size_t len,
265214734Srpaulo							 const char *passwd)
266214734Srpaulo{
267214734Srpaulo	const u8 *pos, *end;
268214734Srpaulo	unsigned char *der;
269214734Srpaulo	size_t der_len;
270214734Srpaulo	struct crypto_private_key *pkey;
271214734Srpaulo
272214734Srpaulo	if (passwd == NULL)
273214734Srpaulo		return NULL;
274214734Srpaulo	pos = search_tag(pem_key_enc_begin, key, len);
275214734Srpaulo	if (!pos)
276214734Srpaulo		return NULL;
277214734Srpaulo	pos += os_strlen(pem_key_enc_begin);
278214734Srpaulo	end = search_tag(pem_key_enc_end, pos, key + len - pos);
279214734Srpaulo	if (!end)
280214734Srpaulo		return NULL;
281214734Srpaulo
282214734Srpaulo	der = base64_decode(pos, end - pos, &der_len);
283214734Srpaulo	if (!der)
284214734Srpaulo		return NULL;
285214734Srpaulo	pkey = crypto_private_key_import(der, der_len, passwd);
286214734Srpaulo	os_free(der);
287214734Srpaulo	return pkey;
288214734Srpaulo}
289214734Srpaulo
290214734Srpaulo
291189251Ssamstatic int tlsv1_set_key(struct tlsv1_credentials *cred,
292214734Srpaulo			 const u8 *key, size_t len, const char *passwd)
293189251Ssam{
294214734Srpaulo	cred->key = crypto_private_key_import(key, len, passwd);
295214734Srpaulo	if (cred->key == NULL)
296214734Srpaulo		cred->key = tlsv1_set_key_pem(key, len);
297214734Srpaulo	if (cred->key == NULL)
298214734Srpaulo		cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
299189251Ssam	if (cred->key == NULL) {
300189251Ssam		wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
301189251Ssam		return -1;
302189251Ssam	}
303189251Ssam	return 0;
304189251Ssam}
305189251Ssam
306189251Ssam
307189251Ssam/**
308189251Ssam * tlsv1_set_private_key - Set private key
309189251Ssam * @cred: TLSv1 credentials from tlsv1_cred_alloc()
310189251Ssam * @private_key: File or reference name for the key in PEM or DER format
311189251Ssam * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
312189251Ssam * passphrase is used.
313189251Ssam * @private_key_blob: private_key as inlined data or %NULL if not used
314189251Ssam * @private_key_blob_len: private_key_blob length
315189251Ssam * Returns: 0 on success, -1 on failure
316189251Ssam */
317189251Ssamint tlsv1_set_private_key(struct tlsv1_credentials *cred,
318189251Ssam			  const char *private_key,
319189251Ssam			  const char *private_key_passwd,
320189251Ssam			  const u8 *private_key_blob,
321189251Ssam			  size_t private_key_blob_len)
322189251Ssam{
323189251Ssam	crypto_private_key_free(cred->key);
324189251Ssam	cred->key = NULL;
325189251Ssam
326189251Ssam	if (private_key_blob)
327189251Ssam		return tlsv1_set_key(cred, private_key_blob,
328214734Srpaulo				     private_key_blob_len,
329214734Srpaulo				     private_key_passwd);
330189251Ssam
331189251Ssam	if (private_key) {
332189251Ssam		u8 *buf;
333189251Ssam		size_t len;
334189251Ssam		int ret;
335189251Ssam
336189251Ssam		buf = (u8 *) os_readfile(private_key, &len);
337189251Ssam		if (buf == NULL) {
338189251Ssam			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
339189251Ssam				   private_key);
340189251Ssam			return -1;
341189251Ssam		}
342189251Ssam
343214734Srpaulo		ret = tlsv1_set_key(cred, buf, len, private_key_passwd);
344189251Ssam		os_free(buf);
345189251Ssam		return ret;
346189251Ssam	}
347189251Ssam
348189251Ssam	return 0;
349189251Ssam}
350189251Ssam
351189251Ssam
352189251Ssamstatic int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
353189251Ssam				  const u8 *dh, size_t len)
354189251Ssam{
355189251Ssam	struct asn1_hdr hdr;
356189251Ssam	const u8 *pos, *end;
357189251Ssam
358189251Ssam	pos = dh;
359189251Ssam	end = dh + len;
360189251Ssam
361189251Ssam	/*
362189251Ssam	 * DHParameter ::= SEQUENCE {
363189251Ssam	 *   prime INTEGER, -- p
364189251Ssam	 *   base INTEGER, -- g
365189251Ssam	 *   privateValueLength INTEGER OPTIONAL }
366189251Ssam	 */
367189251Ssam
368189251Ssam	/* DHParamer ::= SEQUENCE */
369189251Ssam	if (asn1_get_next(pos, len, &hdr) < 0 ||
370189251Ssam	    hdr.class != ASN1_CLASS_UNIVERSAL ||
371189251Ssam	    hdr.tag != ASN1_TAG_SEQUENCE) {
372189251Ssam		wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
373189251Ssam			   "valid SEQUENCE - found class %d tag 0x%x",
374189251Ssam			   hdr.class, hdr.tag);
375189251Ssam		return -1;
376189251Ssam	}
377189251Ssam	pos = hdr.payload;
378189251Ssam
379189251Ssam	/* prime INTEGER */
380189251Ssam	if (asn1_get_next(pos, end - pos, &hdr) < 0)
381189251Ssam		return -1;
382189251Ssam
383189251Ssam	if (hdr.class != ASN1_CLASS_UNIVERSAL ||
384189251Ssam	    hdr.tag != ASN1_TAG_INTEGER) {
385189251Ssam		wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
386189251Ssam			   "class=%d tag=0x%x", hdr.class, hdr.tag);
387189251Ssam		return -1;
388189251Ssam	}
389189251Ssam
390189251Ssam	wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
391189251Ssam	if (hdr.length == 0)
392189251Ssam		return -1;
393189251Ssam	os_free(cred->dh_p);
394189251Ssam	cred->dh_p = os_malloc(hdr.length);
395189251Ssam	if (cred->dh_p == NULL)
396189251Ssam		return -1;
397189251Ssam	os_memcpy(cred->dh_p, hdr.payload, hdr.length);
398189251Ssam	cred->dh_p_len = hdr.length;
399189251Ssam	pos = hdr.payload + hdr.length;
400189251Ssam
401189251Ssam	/* base INTEGER */
402189251Ssam	if (asn1_get_next(pos, end - pos, &hdr) < 0)
403189251Ssam		return -1;
404189251Ssam
405189251Ssam	if (hdr.class != ASN1_CLASS_UNIVERSAL ||
406189251Ssam	    hdr.tag != ASN1_TAG_INTEGER) {
407189251Ssam		wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
408189251Ssam			   "class=%d tag=0x%x", hdr.class, hdr.tag);
409189251Ssam		return -1;
410189251Ssam	}
411189251Ssam
412189251Ssam	wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
413189251Ssam	if (hdr.length == 0)
414189251Ssam		return -1;
415189251Ssam	os_free(cred->dh_g);
416189251Ssam	cred->dh_g = os_malloc(hdr.length);
417189251Ssam	if (cred->dh_g == NULL)
418189251Ssam		return -1;
419189251Ssam	os_memcpy(cred->dh_g, hdr.payload, hdr.length);
420189251Ssam	cred->dh_g_len = hdr.length;
421189251Ssam
422189251Ssam	return 0;
423189251Ssam}
424189251Ssam
425189251Ssam
426189251Ssamstatic const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
427189251Ssamstatic const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
428189251Ssam
429189251Ssam
430189251Ssamstatic int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
431189251Ssam				   const u8 *buf, size_t len)
432189251Ssam{
433189251Ssam	const u8 *pos, *end;
434189251Ssam	unsigned char *der;
435189251Ssam	size_t der_len;
436189251Ssam
437189251Ssam	pos = search_tag(pem_dhparams_begin, buf, len);
438189251Ssam	if (!pos) {
439189251Ssam		wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
440189251Ssam			   "assume DER format");
441189251Ssam		return tlsv1_set_dhparams_der(cred, buf, len);
442189251Ssam	}
443189251Ssam
444189251Ssam	wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
445189251Ssam		   "format");
446189251Ssam
447189251Ssam	pos += os_strlen(pem_dhparams_begin);
448189251Ssam	end = search_tag(pem_dhparams_end, pos, buf + len - pos);
449189251Ssam	if (end == NULL) {
450189251Ssam		wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
451189251Ssam			   "tag (%s)", pem_dhparams_end);
452189251Ssam		return -1;
453189251Ssam	}
454189251Ssam
455189251Ssam	der = base64_decode(pos, end - pos, &der_len);
456189251Ssam	if (der == NULL) {
457189251Ssam		wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
458189251Ssam		return -1;
459189251Ssam	}
460189251Ssam
461189251Ssam	if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
462189251Ssam		wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
463189251Ssam			   "DER conversion");
464189251Ssam		os_free(der);
465189251Ssam		return -1;
466189251Ssam	}
467189251Ssam
468189251Ssam	os_free(der);
469189251Ssam
470189251Ssam	return 0;
471189251Ssam}
472189251Ssam
473189251Ssam
474189251Ssam/**
475189251Ssam * tlsv1_set_dhparams - Set Diffie-Hellman parameters
476189251Ssam * @cred: TLSv1 credentials from tlsv1_cred_alloc()
477189251Ssam * @dh_file: File or reference name for the DH params in PEM or DER format
478189251Ssam * @dh_blob: DH params as inlined data or %NULL if not used
479189251Ssam * @dh_blob_len: dh_blob length
480189251Ssam * Returns: 0 on success, -1 on failure
481189251Ssam */
482189251Ssamint tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
483189251Ssam		       const u8 *dh_blob, size_t dh_blob_len)
484189251Ssam{
485189251Ssam	if (dh_blob)
486189251Ssam		return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
487189251Ssam
488189251Ssam	if (dh_file) {
489189251Ssam		u8 *buf;
490189251Ssam		size_t len;
491189251Ssam		int ret;
492189251Ssam
493189251Ssam		buf = (u8 *) os_readfile(dh_file, &len);
494189251Ssam		if (buf == NULL) {
495189251Ssam			wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
496189251Ssam				   dh_file);
497189251Ssam			return -1;
498189251Ssam		}
499189251Ssam
500189251Ssam		ret = tlsv1_set_dhparams_blob(cred, buf, len);
501189251Ssam		os_free(buf);
502189251Ssam		return ret;
503189251Ssam	}
504189251Ssam
505189251Ssam	return 0;
506189251Ssam}
507