Plan 9 from Bell Labs’s /usr/web/sources/contrib/lucio/asn1/pem/pemout.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
**	@(#) pemout.c - PEM encoding
**	@(#) $Id: pemout.c,v 1.10 2003/12/08 07:15:17 lucio Exp $
*/

/*
** ==================================================================
**
**      $Logfile:$
**      $RCSfile: pemout.c,v $
**      $Revision: 1.10 $
**      $Date: 2003/12/08 07:15:17 $
**      $Author: lucio $
**
** ==================================================================
**
**      $Log: pemout.c,v $
**      Revision 1.10  2003/12/08 07:15:17  lucio
**      Weekend developments - mostly OID related
**
**      Revision 1.9  2003/12/04 16:13:31  lucio
**      Checkpoint - OID rethink (incomplete)
**
**      Revision 1.8  2003/12/04 11:31:43  lucio
**      Streamlined - specially OID management
**
**      Revision 1.7  2003/11/30 19:05:27  lucio
**      Advanced - plenty to go still, of course.
**
**      Revision 1.6  2003/11/27 19:19:37  lucio
**      Checkpoint - DNs almost complete
**
**      Revision 1.5  2003/11/27 18:39:27  lucio
**      Checkpoint - some progress with DNs
**
**      Revision 1.4  2003/11/25 14:35:29  lucio
**      Checkpoint to take home.
**
**      Revision 1.3  2003/11/25 08:43:19  lucio
**      On return from home
**
**      Revision 1.2  2003/11/24 17:45:59  lucio
**      Checkpoint after some progress
**
**      Revision 1.1.1.1  2003/11/10 10:34:31  lucio
**      ASN.1 developments.
**
** ==================================================================
*/

#include <u.h>
#include <libc.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <mp.h>
#include <libsec.h>

#include "b64.h"
#include "../devel/ber.h"
#include "../devel/oid.h"

static void
copyright (void)
{
	print ("@(#) pemout: PEM (RFC 1424) encoding\n");
	print ("@(#) $Id: pemout.c,v 1.10 2003/12/08 07:15:17 lucio Exp $\n");
	print ("@(#) Copyright (C) 2003 Lucio De Re.\n");
	print ("@(#) Author (A) Lucio De Re, 2003.\n");
}

static  char *use[] = {
    "usage: %s [-h|H] [-p PEMtype] [-f originator]... [-t recipient]...\n",
    "\n",
    "opts: -h|H:	this message\n",
    "      -V:		show version and copyright notice\n",
    "\n",
    "      -p PEMtype:		Processing type\n",
	"      -f originator:	Originator mail\n",
	"      -t recipient:	Recipient mail\n",
    nil
};

static void
usage (char *argv0, char *use) {
	fprint (2, use, argv0);
	exits ("usage");
}

static void
help (char *argv0, char **use) {
    print (*use++, argv0);
    while (*use) {
        print (*use++);
    }
}

enum {
	MIC_CLEAR,
	MIC_ONLY,
	ENCRYPTED,
	CRL,
};

static char *proctype[] = {
	[MIC_CLEAR]	"MIC-CLEAR",
	[MIC_ONLY]	"MIC-ONLY",
	[ENCRYPTED]	"ENCRYPTED",
	[CRL]		"CRL",
	nil,
};

enum {
	DES_CBC,
};

static char *dekinfo[] = {
	[DES_CBC]	"DES-CBC",
};

enum {
	RSA,
};

static char *keyinfo[] = {
	[RSA]		"RSA",
};

/*
	We could use upas/fs to fragment the message into the
	components we require, namely the originator(s) and
	recipient(s) and the message body.  We may however prefer to
	allow the explicit specification of the parties involved on
	the command line as possibly multiple entries.  In any case,
	the headers, if supplied, will be left unmodified.

*/

static OidHier *hierarchy;

/*
	Search the database for the given address, return the
	information required to process the message as specified by
	the "ptype" argument.  The type of returned information varies
	with the party, rather than the processing type: the PEM RFCs
	only expand on asymmetric processing, where an X.509
	certificate is used, although they provide the infrastructure
	for other approaches.

	Like them, we'll probably only consider X.509 certificates,
	but ensure that the procedures can be extended to cater for
	alternatives.

	We need to return not only the certificate, but also the
	available encryption methods, possibly other details.  RFC
	1422 refers.

ber_obj *
getauth (char *addr, int ptype) {
}
*/

typedef struct target Target;
struct target {
	char *addr;
	char *name;
	int nname;
	int *cap;
	uchar *cert;
	int ncert;
	uchar *icert;
	int nicert;
	RSApub* pub; 
	int ktype;
	uchar *key;
	long keysz;
	Target *next;
};

static char *ldr_cert[] = {
	"MIIBazCB1QIBADANBgkqhkiG9w0BAQQFABAAMB4XDTAzMTEzMDE2MjgwNFoXDTA2",
	"MTIwMjE2MjgwNFoQADCBnDANBgkqhkiG9w0BAQEFAAOBigAwgYYCgYCgVhPJm6Qj",
	"Zt0X9XNfzd+60c6f7/P2M7b0DZQCQ9T8/xS/mL+LeBbkyVFIkZnbXTHZu6VauKMM",
	"29NudWVytZLDuUlFJYM7cdY20N6+hLHMBmyZJubWZ1xEoFWi+zi8VZU9wiB/kbJp",
	"Hxd/RXZtlb/6v9idljj8TKZnDF13ob427QIBIzANBgkqhkiG9w0BAQQFAAOBgQAD",
	"TtReORNcCfVCTb5ikzehOhimEwn1Wz+LJ5l0tnaGs0ulpOEJTsSSxMW+LUHOdTGm",
	"4kgI18+PomN/Z+LjsWm5Bq9zlrIi1l41urevU+ozTxCia5rZOQahJbR9MeWYkGQd",
	"LwG+cf8Q9OoJ7oquNKLjQOWyZ6a5fMke2ScOBLO4DA==",
	nil,
};

static char *o_cert[] = {
	"MIIBlTCCAScCAWUwDQYJKoZIhvcNAQECBQAwUTELMAkGA1UEBhMCVVMxIDAeBgNV",
	"BAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMQ8wDQYDVQQLEwZCZXRhIDExDzAN",
	"BgNVBAsTBk5PVEFSWTAeFw05MTA5MDQxODM4MTdaFw05MzA5MDMxODM4MTZaMEUx",
	"CzAJBgNVBAYTAlVTMSAwHgYDVQQKExdSU0EgRGF0YSBTZWN1cml0eSwgSW5jLjEU",
	"MBIGA1UEAxMLVGVzdCBVc2VyIDEwWTAKBgRVCAEBAgICAANLADBIAkEAwHZHl7i+",
	"yJcqDtjJCowzTdBJrdAiLAnSC+CnnjOJELyuQiBgkGrgIh3j8/x0fM+YrsyF1u3F",
	"LZPVtzlndhYFJQIDAQABMA0GCSqGSIb3DQEBAgUAA1kACKr0PqphJYw1j+YPtcIq",
	"iWlFPuN5jJ79Khfg7ASFxskYkEMjRNZV/HZDZQEhtVaU7Jxfzs2wfX5byMp2X3U/",
	"5XUXGx7qusDgHQGs7Jk9W8CW1fuSWUgN4w==",
	nil,
};

static char *i_cert[] = {
	"MIIB3DCCAUgCAQowDQYJKoZIhvcNAQECBQAwTzELMAkGA1UEBhMCVVMxIDAeBgNV",
	"BAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMQ8wDQYDVQQLEwZCZXRhIDExDTAL",
	"BgNVBAsTBFRMQ0EwHhcNOTEwOTAxMDgwMDAwWhcNOTIwOTAxMDc1OTU5WjBRMQsw",
	"CQYDVQQGEwJVUzEgMB4GA1UEChMXUlNBIERhdGEgU2VjdXJpdHksIEluYy4xDzAN",
	"BgNVBAsTBkJldGEgMTEPMA0GA1UECxMGTk9UQVJZMHAwCgYEVQgBAQICArwDYgAw",
	"XwJYCsnp6lQCxYykNlODwutF/jMJ3kL+3PjYyHOwk+/9rLg6X65B/LD4bJHtO5XW",
	"cqAz/7R7XhjYCm0PcqbdzoACZtIlETrKrcJiDYoP+DkZ8k1gCk7hQHpbIwIDAQAB",
	"MA0GCSqGSIb3DQEBAgUAA38AAICPv4f9Gx/tY4+p+4DB7MV+tKZnvBoy8zgoMGOx",
	"dD2jMZ/3HsyWKWgSF0eH/AJB3qr9zosG47pyMnTf3aSy2nBO7CMxpUWRBcXUpE+x",
	"EREZd9++32ofGBIXaialnOgVUn0OzSYgugiQ077nJLDUj0hQehCizEs5wUJ35a5h",
	nil,
};

static void
dcert (Target *fp) {
	int w, n = 0;
	char *t, *t0, **t1;

	t = malloc (w = 512);
	for (t1 = ldr_cert; *t1; t1++) {
		if (n + strlen (*t1) > w) {
			w *= 2;
			t = realloc (t, w);
		}
		t0 = t + n;
		n += db64v ((uchar *) t0, *t1);
	}
	fp->cert = realloc (t, fp->ncert = n);
	n = 0;
	t = malloc (w = 512);
	for (t1 = i_cert; *t1; t1++) {
		if (n + strlen (*t1) > w) {
			w *= 2;
			t = realloc (t, w);
		}
		t0 = t + n;
		n += db64v ((uchar *) t0, *t1);
	}
	fp->icert = realloc (t, fp->nicert = n);
	fp->name = malloc (fp->nname = 128);
	fp->ktype = RSA;
	t = malloc (128);
	n = db64v ((uchar *) t, "I3rRIGXUGWAF8js5wCzRTkdhO34PTHdRZY9Tuvm03M+NM7fx6qc5udixps2Lng0+");
	t0 = t + n;
    n += db64v ((uchar *) t0, "wGrtiUm/ovtKdinz6ZQ/aQ==");
	fp->key = realloc (t, n);
	fp->keysz = n;
}

static void
cert_analyse (BerObj *op) {
	BerObj *opd, *op1;
	OidObj aug;

	if (!op)
		return;
ber_print (op, 1);
	print ("Certificate\n");
	if (opd = op->down) {		// constructed record
		if (opd->tag == BER_TAG_INTEGER) {
			if (opd->next->tag == BER_TAG_INTEGER) {
				print ("\tVersion = %I\n", opd);
				opd = opd->next;
			} else {
				print ("\tVersion = 0\n");
			}
			print ("\tSerial = %I\n", opd);
		} else {
			print ("Unrecognised BER object (tag = %d)\n", opd->tag);
			return;
		}
		opd = opd->next;
		print ("\tSignature:\n");
		op1 = opd->down;
		print ("\t\tAlgorithm: %O\n", op1);
/*
		if (memcmp (op1->buf, BER_MD2RSAENCRYPTION_OBJECTID, op1->len) == 0) {
			print ("\t\tParameters: NULL\n");
		} else {
			print ("Unrecognised algorithm\n");
			return;
		}
	This should read:

		if (oid_isobj (op1->buf, BER_MD2RSAENCRYPTION_OBJECTID)) {
		}
		...
	where the constant is somehow obtained from our OID database.  Alternatively, we could search the database for the corresponding object name:

		n = oid_name (h, op1->buf);
		if (n != nil && strcmp (n, BER_MD2RSAENCRYPTION_OBJECTNAME) == 0) {
		}
		...

	Neither seems convenient, but one of them will have to do
	unless we choose to implement everything in C++ (where
	operator overloading will make things considerably simpler, if
	more risky).

	What we actually need is an opaque mechanism to
	retrieve/return the algorithm and parameters in the format
	most appropriate for our needs.  Right now, this is for
	printing purposes, but in the long term we will probably need
	the details to perform some operation on the associated text.
	All along, there seems to be a need to parse the ASN.1 object
	in terms of the objectives rather than in a generic fashion if
	one is to avoid redundant processing, but that way leads to
	producing specialised utilities that do not adjust readily to
	changing conditions.  In particular, I'm hoping to entend the
	PEM encoder/decoder into its equivalent S/MIME processor
	without writing an entirely new parser.  Certainly, it is
	necessary to proceed with the immediate requirements before
	being able to generalise the needs: trying to establish from
	theory and first principles what is required is not an option
	because the specifications are too difficult to interpret in a
	vacuum.

	Alternatively to all of the above, memcmp() may still be the
	best approach.  After all, we do have the parsed object and
	its BER representation and what is missing - that we used to
	have, one iteration back - is a record of the object we
	generated (at the time it was being generated on purpose, now
	we make better use of it).  There are other considerations
	that need investigating...

	Presently, the hierarchy/database (all right, then, it's a
	hierarchical database!) is constructed by private functions
	oid_build() and oid_append() (great deal of details in the
	oid.c module file) which ensure by devious means that the
	handle to the database is kept up to date even when a new root
	entry is added.  Lower down in the functionality layers, a
	more orthodox mechanism (by some measurement of orthodox) is
	used, making it possible to keep the hierarchy accessible
	_and_ return a pointer to the node entry being operated upon.
	It seems necessary to extend the latter mechanism to
	oid_build() and oid_append() so that here we can also enhance
	the return capabilities.  In particular, right now, we want to
	be able to retrieve the BER representation of an OID (we may
	even make good use of this facility in constructing OID
	objects later) either at creation time or on demand when we
	want to identify an OID as we do here.

	In practice, we're currently looking at algorithm oIDs as well
	as the accompanying, required parameters.  We can't just yet
	use the OID database to determine what parameters to associate
	with a particular algorithm, but we certainly want to be able
	to establish whether the algorithm is what we desire/expect it
	to be.  I guess it is up to us then to decide what to do if it
	is or isn't.  In some fashion it ought to be possible to put
	into an extended database the actual semantics of
	certificates, but that has to be a specialised function rather
	than a generic solution.  Something to give serious attention
	to, certainly.

	Another option?

		switch (oid_algid (op1->buf, algs)) {
			case OID_MD2RSAENCRYPTION:
				do_MD2();
				break;
			...
			default:
				fprint (2, "Not a valid algorithm\n");
				exits ("alg");
		}

	where algs[] is a set of descriptors for valid algorithms and
	the return value from oid_algid() is the index into algs[] or
	-1 if not found.  This does take care of determining what
	algorithms are valid and which one of them was specified.  The
	actual semantics can then be obtained via the chosen algs[]
	element and will have been established, possibly separately
	from some configuration information retrieved at startup time.

	Mind you, whenever I think startup, I wonder how it can be
	turned into a file system.  I don't think it's in Bell Labs'
	philosophy, but one benefit of using a files system to provide
	services is the advantage that it needs only occasional
	initialisation, unlike procedures that need to read their
	configuration information whenever they are utilised.

	In this vein, figuring out how to install the certificate
	extraction/creation facility as a file server may not be easy,
	but it ought to be well worth the effort.  Tentatively, one
	would submit the necessary certificate(s) (to factotum,
	really!) and then pipe the data through the server to obtain
	the desired result.
*/
		opd = opd->next;
		aug.obj = opd->down;
		aug.hier = hierarchy;
		print ("\tIssuer: %N\n", &aug);
		opd = opd->next;
		print ("\tValidity:\n");
		print ("\t\tnotBefore: %T\n", op1 = opd->down);
		print ("\t\tnotAfter: %T\n", op1->next);
		opd = opd->next;
		aug.obj = opd->down;
		aug.hier = hierarchy;
		print ("\tSubject: %N\n", &aug);
		opd = opd->next;
		print ("\tPublicKey:\n");
		op1 = opd->down;
		print ("\t\tAlgorithm: %O\n", op1->down);
/*
		print ("\t\t\tAlgorithm: %O\n", opd->down);
		print ("\t\t\tParameters: %d\n", o->...); // dependent on above
*/
	} else {
		print ("Unrecognisable certificate\n");
		return;
	}
}

static Target * 
append (Target *t, char *name) {
	Target *t0 = malloc (sizeof (Target));
	Target *t1, *t2;

	t0->addr = strdup (name);
	t0->cap = nil;
	t0->next = nil;
	dcert (t0);					// fake certificate
	if (t == nil) {
		return t0;
	}
	for (t1 = t; t1; t1 = t1->next) t2 = t1;
	t2->next = t0;
	return t;
}

void
main (int argc, char *argv[]) {
	int ptype = ENCRYPTED, dektype = DES_CBC, x;
	uchar dekval[8];
	char *optarg, **p0, *msg;
	Target *from = nil, *to = nil, *fp;
	BerObj *o, *o0;

	ARGBEGIN {
		case 'p':		// transformation type (proctype[])
			optarg = EARGF(usage (argv0, *use));
			for (ptype = 0, p0 = proctype; *p0; p0++, ptype++) {
				if (cistrncmp (optarg, *p0, strlen (*p0)) == 0)
					break;
			}
			if (*p0 != nil) {
				fprint (2, "%s: Invalid processing type: %s\n", argv0, optarg);
				exits ("proctype");
			}
			break;
		case 'f':		// one or more originators
			from = append (from, EARGF(usage (argv0, *use)));
			break;
		case 't':		// one or more recipients
			to = append (to, EARGF(usage (argv0, *use)));
			break;
		default:
			break;
	} ARGEND
	quotefmtinstall();
	doquote = needsrcquote;
	hierarchy = oid_initdb (nil);
	print ("-----BEGIN PRIVACY-ENHANCED MESSAGE-----\r\n");
	print ("Proc-Type: %d,%s\r\n", 4, proctype[ptype]);
	print ("Content-Domain: RFC822\r\n");
	switch (ptype) {
		case ENCRYPTED:
			switch (dektype) {
				case DES_CBC:
					des56to64 ((uchar *) "abcdefg", dekval);
					print ("DEK-Info: %s,", dekinfo[dektype]);
					for (x = 0; x < sizeof (dekval); x++) {
						print ("%02X", dekval[x] & 0xFF);
					}
					print ("\r\n");
					for (fp = from; fp; fp = fp->next) {	// originator(s)
						print ("Mail from: %s <%s>\r\n", fp->name, fp->addr);
						print ("Originator-Certificate:\r\n");
						feb64n (" %s\r\n", fp->cert, fp->ncert, 48);
						print ("Key-Info: %s,\r\n", keyinfo[fp->ktype]);
						feb64n (" %s\r\n", fp->key, fp->keysz, 48);
	// check signature of certificate
	// extract and display elements of certificate
	// report on validity of certificate
	fmtinstall ('I', ber_fmtint);
	fmtinstall ('N', oid_fmtdn);
	fmtinstall ('O', ber_fmtoid);
	fmtinstall ('T', ber_fmtdate);
//asn1dump (fp->cert, fp->ncert);
	if (o = ber_parse (fp->cert, fp->cert + fp->ncert)) {
		fp->pub = X509toRSApub ((uchar *) (o->down->buf), o->down->len, nil, 0);
		if (msg = X509verify (fp->cert, fp->ncert, fp->pub)) {
			fprint (2, "Originator certificate does not verify:\n\t%s\n", msg);
			// exits ("orig cert");
		}
		// ber_print (o, 1);
		cert_analyse (o->down);
		// ber_free (o);
	} else {
		fprint (2, "Originator certificate cannot be parsed\n");
		exits ("origcert");
	}
						print ("Issuer-Certificate:\r\n");
						feb64n (" %s\r\n", fp->icert, fp->nicert, 48);
//asn1dump (fp->icert, fp->nicert);
	if (o0 = ber_parse (fp->icert, fp->icert + fp->nicert)) {
		fp->pub = X509toRSApub ((uchar *) (o0->down->buf), o0->down->len, nil, 0);
		if (msg = X509verify ((uchar *) (o0->down->buf), o0->down->len, fp->pub)) {
			fprint (2, "Issuer certificate does not verify:\n\t%s\n", msg);
			// exits ("iss cert");
		}
	
			cert_analyse (o0->down);
		// ber_free (o0);
	} else {
		fprint (2, "Originator certificate cannot be parsed\n");
		exits ("origcert");
	}
					}
					for (fp = to; fp; fp = fp->next) {	// recipient(s)
						print ("Rcpt to: %s <%s>\r\n", fp->name, fp->addr);
						print ("Originator-Certificate:\r\n");
						feb64n (" %s\r\n", fp->cert, fp->ncert, 48);
						print ("Key-Info: %s,\r\n", keyinfo[fp->ktype]);
						feb64n (" %s\r\n", fp->key, fp->keysz, 48);
						print ("Issuer-Certificate:\r\n");
					}
					break;
				default:
					fprint (2, "%s: invalid encryption algorithm: %d\n", argv0, dektype);
					break;
			}
if (0) {
			/*
				generate a 56-bit (plus odd parity bits) key for DES-CBC (RFC-1423 does not consider other options), 
			*/
/*
**     MIIBlTCCAScCAWUwDQYJKoZIhvcNAQECBQAwUTELMAkGA1UEBhMCVVMxIDAeBgNV
**     BAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMQ8wDQYDVQQLEwZCZXRhIDExDzAN
**     BgNVBAsTBk5PVEFSWTAeFw05MTA5MDQxODM4MTdaFw05MzA5MDMxODM4MTZaMEUx
**     CzAJBgNVBAYTAlVTMSAwHgYDVQQKExdSU0EgRGF0YSBTZWN1cml0eSwgSW5jLjEU
**     MBIGA1UEAxMLVGVzdCBVc2VyIDEwWTAKBgRVCAEBAgICAANLADBIAkEAwHZHl7i+
**     yJcqDtjJCowzTdBJrdAiLAnSC+CnnjOJELyuQiBgkGrgIh3j8/x0fM+YrsyF1u3F
**     LZPVtzlndhYFJQIDAQABMA0GCSqGSIb3DQEBAgUAA1kACKr0PqphJYw1j+YPtcIq
**     iWlFPuN5jJ79Khfg7ASFxskYkEMjRNZV/HZDZQEhtVaU7Jxfzs2wfX5byMp2X3U/
**     5XUXGx7qusDgHQGs7Jk9W8CW1fuSWUgN4w==
*/
/*
**     I3rRIGXUGWAF8js5wCzRTkdhO34PTHdRZY9Tuvm03M+NM7fx6qc5udixps2Lng0+
**     wGrtiUm/ovtKdinz6ZQ/aQ==
*/
/*
**     MIIB3DCCAUgCAQowDQYJKoZIhvcNAQECBQAwTzELMAkGA1UEBhMCVVMxIDAeBgNV
**     BAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMQ8wDQYDVQQLEwZCZXRhIDExDTAL
**     BgNVBAsTBFRMQ0EwHhcNOTEwOTAxMDgwMDAwWhcNOTIwOTAxMDc1OTU5WjBRMQsw
**     CQYDVQQGEwJVUzEgMB4GA1UEChMXUlNBIERhdGEgU2VjdXJpdHksIEluYy4xDzAN
**     BgNVBAsTBkJldGEgMTEPMA0GA1UECxMGTk9UQVJZMHAwCgYEVQgBAQICArwDYgAw
**     XwJYCsnp6lQCxYykNlODwutF/jMJ3kL+3PjYyHOwk+/9rLg6X65B/LD4bJHtO5XW
**     cqAz/7R7XhjYCm0PcqbdzoACZtIlETrKrcJiDYoP+DkZ8k1gCk7hQHpbIwIDAQAB
**     MA0GCSqGSIb3DQEBAgUAA38AAICPv4f9Gx/tY4+p+4DB7MV+tKZnvBoy8zgoMGOx
**     dD2jMZ/3HsyWKWgSF0eH/AJB3qr9zosG47pyMnTf3aSy2nBO7CMxpUWRBcXUpE+x
**     EREZd9++32ofGBIXaialnOgVUn0OzSYgugiQ077nJLDUj0hQehCizEs5wUJ35a5h
*/
}
	}
	print ("\r\n");
if (0) {
	char *s = nil;
	while (*s) {	// encode to base64
	}
}
	print ("-----END PRIVACY-ENHANCED MESSAGE-----\r\n");
	exits (0);
}
#ifdef LATER
	/*
		Retrieve from "the" database the details that make it
		possible to process the message to suit each recipient
		as well as originator.  Other than an explicit
		preference, we may need to calculate an implicit
		operation based on the intersection of facilities
		available to the originator and recipient.

		It may be most logical to start with the originators
		and establish the most suitable "method" shared
		amongst them (I think this is essential - it probably
		will need to be a list, in some preference sequence),
		then establish for each recipient what is the method
		to be applied.  It is important not to weaken the
		security in the quest to accommodate originators or
		recipients.  In particular, the requested level of
		processing has to be obeyed; even exceeding it is not
		permissible.
	*/
	for (fp = *from; fp; fp = fp->m_next) {
		if ((cp = getauth (fp->addr, ptype)) {
		}
	}
	while (*mp) {	// - encode message to canonical form
					// - apply the requested transformation
					// - output
	}	
#endif

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.