Commit message: Rework the wildly undocumented NSS password file goo.

This probably doesn't work yet.
CVE: CVE-2022-1249
Vulnerability description: A NULL pointer dereference flaw was found in pesign's cms_set_pw_data() function of the cms_common.c file. The function fails to handle the NULL pwdata invocation from daemon.c, which leads to an explicit NULL dereference and crash on all attempts to daemonize pesign.

Project: pesign
File: daemon.c

set_up_socket(context *ctx)
{
	int sd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (sd < 0) {
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_ERR,
			"unable to create socket: %m");
		exit(1);
	}

	struct sockaddr_un addr_un = {
		.sun_family = AF_UNIX,
		.sun_path = SOCKPATH,
	};

	int rc = bind(sd, &addr_un, sizeof(addr_un));
	if (rc < 0) {
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_ERR,
			"unable to bind to \"%s\": %m",
			addr_un.sun_path);
		exit(1);
	}
	rc = chmod(SOCKPATH, 0660);
	if (rc < 0) {
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_ERR,
			"could not set permissions for \"%s\": %m",
			SOCKPATH);
		exit(1);
	}

	rc = listen(sd, 5);
	if (rc < 0) {
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_ERR,
			"unable to listen on socket: %m");
		exit(1);
	}


	ctx->sd = sd;
	return 0;
}

Project: pesign
File: daemon.c

send_response(context *ctx, cms_context *cms, struct pollfd *pollfd, int32_t rc)
{
	struct msghdr msg;
	struct iovec iov;
	ssize_t n;
	int msglen = ctx->errstr ? strlen(ctx->errstr) + 1 : 0;

	iov.iov_len = sizeof(pesignd_msghdr) + sizeof(pesignd_cmd_response)
			+ msglen;

	void *buffer = calloc(1, iov.iov_len);
	if (!buffer) {
		cms->log(cms, ctx->priority|LOG_ERR,
			"could not allocate memory: %m");
		exit(1);
	}

	iov.iov_base = buffer;

	pesignd_msghdr *pm = buffer;
	pesignd_cmd_response *resp = (pesignd_cmd_response *)((uint8_t *)pm +
					offsetof(pesignd_msghdr, size) +
					sizeof (pm->size));

	pm->version = PESIGND_VERSION;
	pm->command = CMD_RESPONSE;
	pm->size = sizeof(resp->rc) + msglen;

	memset(&msg, '\0', sizeof(msg));
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	resp->rc = rc;
	if (ctx->errstr)
		memcpy(resp->errmsg, ctx->errstr, msglen);

	n = sendmsg(pollfd->fd, &msg, 0);
	if (n < 0)
		cms->log(cms, ctx->priority|LOG_WARNING,
			"could not send response to client: %m");

	free(buffer);
}

Project: pesign
File: cms_common.c

void cms_set_pw_data(cms_context *cms, secuPWData *pwdata)
{
	ingress();

	switch (cms->pwdata.source) {
	case PW_SOURCE_INVALID:
	case PW_PROMPT:
	case PW_DEVICE:
	case PW_SOURCE_MAX:
		break;

	case PW_FROMENV:
	case PW_FROMFILEDB:
	case PW_PLAINTEXT:
		memset(cms->pwdata.data, 0, strlen(cms->pwdata.data));
		xfree(cms->pwdata.data);
		break;

	case PW_DATABASE:
		xfree(cms->pwdata.data);
		break;
	}
	memmove(&cms->pwdata, pwdata, sizeof(*pwdata));

	dprintf("pwdata:%p", pwdata);
	dprintf("pwdata->source:%d", pwdata->source);
	dprintf("pwdata->data:%p (\"%s\")", pwdata->data,
		pwdata->data ? pwdata->data : "(null)");
	egress();
}

Project: pesign
File: password.c

static int token_pass_cmp(const void *tp0p, const void *tp1p)
{
	const struct token_pass * const tp0 = (const struct token_pass * const)tp0p;
	const struct token_pass * const tp1 = (const struct token_pass * const)tp1p;
	int rc;

	if (!tp1->token || !tp0->token)
		return tp1->token - tp0->token;
	rc = strcmp(tp0->token, tp1->token);
	if (rc == 0)
		rc = strcmp(tp0->pass, tp1->pass);
	return rc;
}

Project: pesign
File: pesign.c

print_flag_name(FILE *f, int flag)
{
	for (int i = 0; flag_names[i].flag != FLAG_LIST_END; i++) {
		if (flag_names[i].flag == flag)
			fprintf(f, "%s ", flag_names[i].name);
	}
}

Project: pesign
File: password.c

check_password(char *cp)
{
	unsigned int i;

	if (cp == NULL)
		return PR_FALSE;

	for (i = 0; cp[i] != 0; i++) {
		if (!isprint(cp[i]))
			return PR_FALSE;
	}
	if (i == 0)
		return PR_FALSE;
	return PR_TRUE;
}

Project: pesign
File: cms_common.c

make_context_specific(cms_context *cms, int ctxt, SECItem *encoded,
			SECItem *original)
{
	void *rv;
	ContextSpecificSequence[0].kind = SEC_ASN1_EXPLICIT |
					  SEC_ASN1_CONTEXT_SPECIFIC | ctxt;

	rv = SEC_ASN1EncodeItem(cms->arena, encoded, original,
				ContextSpecificSequence);
	if (rv == NULL)
		cmsreterr(-1, cms, "could not encode context specific data");
	return 0;
}

Project: pesign
File: cms_common.c

generate_name(cms_context *cms, SECItem *der, CERTName *certname)
{
	void *marka = PORT_ArenaMark(cms->arena);
	CERTRDN **rdns = certname->rdns;
	CERTRDN *rdn;

	int num_items = 0;
	int rc = 0;

	while (rdns && (rdn = *rdns++) != NULL) {
		CERTAVA **avas = rdn->avas;
		CERTAVA *ava;
		while (avas && (ava = *avas++) != NULL)
			num_items++;
	}

	if (num_items == 0) {
		PORT_ArenaRelease(cms->arena, marka);
		cmsreterr(-1, cms, "No name items to encode");
	}

	SECItem items[num_items];

	int i = 0;
	rdns = certname->rdns;
	while (rdns && (rdn = *rdns++) != NULL) {
		CERTAVA **avas = rdn->avas;
		CERTAVA *ava;
		while (avas && (ava = *avas++) != NULL) {
			SECItem avader;
			rc = generate_ava(cms, &avader, ava);
			if (rc < 0) {
				PORT_ArenaRelease(cms->arena, marka);
				return -1;
			}

			SECItem *list[2] = {
				&avader,
				NULL,
			};
			rc = wrap_in_set(cms, &items[i], list);
			if (rc < 0) {
				PORT_ArenaRelease(cms->arena, marka);
				return -1;
			}
			i++;
		}
	}
	wrap_in_seq(cms, der, &items[0], num_items);
	PORT_ArenaUnmark(cms->arena, marka);

	return 0;
}

Project: pesign
File: daemon.c

quit_handler(int signal UNUSED)
{
	should_exit = 1;
}

Project: pesign
File: signer_info.c

generate_authvar_signer_info(cms_context *cms, SpcSignerInfo *sip)
{
	SpcSignerInfo si;
	SECItem buf;

	if (!sip)
		return -1;

	memset(&si, '\0', sizeof (si));

	if (SEC_ASN1EncodeInteger(cms->arena, &si.CMSVersion, 1) == NULL) {
		cms->log(cms, LOG_ERR, "could not encode CMSVersion: %s",
			PORT_ErrorToString(PORT_GetError()));
		goto err;
	}

	si.sid.signerType = signerTypeIssuerAndSerialNumber;
	si.sid.signerValue.iasn.issuer = cms->cert->derIssuer;
	si.sid.signerValue.iasn.serial = cms->cert->serialNumber;

	if (generate_algorithm_id(cms, &si.digestAlgorithm,
			digest_get_digest_oid(cms)) < 0)
		goto err;

	si.signedAttrs.len = 0;
	si.signedAttrs.data = NULL;

	buf.len = cms->authbuf_len;
	buf.data = cms->authbuf;
	if (sign_blob(cms, &si.signature, &buf) < 0)
		goto err;

	if (generate_algorithm_id(cms, &si.signatureAlgorithm,
				digest_get_encryption_oid(cms)) < 0)
		goto err;

	si.unsignedAttrs.len = 0;
	si.unsignedAttrs.data = NULL;

	memcpy(sip, &si, sizeof(si));
	return 0;
err:
	return -1;
}

Project: pesign
File: cms_common.c

generate_spc_string(cms_context *cms, SECItem *ssp, char *str, int len)
{
	SpcString ss;
	memset(&ss, '\0', sizeof (ss));

	SECITEM_AllocItem(cms->arena, &ss.unicode, len);
	if (len != 0) {
		if (!ss.unicode.data)
			cmsreterr(-1, cms, "could not allocate memory");

		memcpy(ss.unicode.data, str, len);
	}
	ss.unicode.type = siBMPString;

	if (SEC_ASN1EncodeItem(cms->arena, ssp, &ss, SpcStringTemplate) == NULL)
		cmsreterr(-1, cms, "could not encode SpcString");

	return 0;
}

Project: pesign
File: daemon.c

handle_sign_helper(context *ctx, struct pollfd *pollfd, socklen_t size,
		   int attached, bool with_file_type)
{
	int rc = cms_context_alloc(&ctx->cms);
	if (rc < 0)
		return;

	steal_from_cms(ctx->backup_cms, ctx->cms);

	handle_signing(ctx, pollfd, size, attached, with_file_type);

	hide_stolen_goods_from_cms(ctx->cms, ctx->backup_cms);
	cms_context_fini(ctx->cms);
}

Project: pesign
File: daemon.c

handle_is_token_unlocked(context *ctx, struct pollfd *pollfd, socklen_t size)
{
	struct msghdr msg;
	struct iovec iov;
	ssize_t n;

	int rc = cms_context_alloc(&ctx->cms);
	if (rc < 0) {
		send_response(ctx, ctx->backup_cms, pollfd, rc);
		return;
	}

	steal_from_cms(ctx->backup_cms, ctx->cms);

	char *buffer = malloc(size);
	if (!buffer) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"unable to allocate memory: %m");
		exit(1);
	}

	memset(&msg, '\0', sizeof(msg));

	iov.iov_base = buffer;
	iov.iov_len = size;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	n = recvmsg(pollfd->fd, &msg, MSG_WAITALL);

	pesignd_string *tn = (pesignd_string *)buffer;
	if (n < (long long)sizeof(tn->size)) {
malformed:
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"unlock-token: invalid data");
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"possible exploit attempt. closing.");
		close(pollfd->fd);
		return;
	}
	n -= sizeof(tn->size);
	if ((size_t)n < tn->size)
		goto malformed;
	n -= tn->size;

	if (tn->value[tn->size - 1] != '\0')
		goto malformed;

	if (n != 0)
		goto malformed;

	ctx->cms->log(ctx->cms, ctx->priority|LOG_NOTICE,
		"querying token \"%s\"", tn->value);

	char *key = (char *)tn->value;
	char *tokenname;

	tokenname = bsearch(&key, ctx->tokennames, ctx->ntokennames,
				sizeof (char *), cmpstringp);
	send_response(ctx, ctx->cms, pollfd, tokenname == NULL ? 1 : 0);

	ctx->cms->log(ctx->cms, ctx->priority|LOG_NOTICE,
			"token \"%s\" is %sunlocked", tn->value,
			tokenname == NULL ? "not " : "");

	free(buffer);

	hide_stolen_goods_from_cms(ctx->cms, ctx->backup_cms);
	cms_context_fini(ctx->cms);
}

Project: pesign
File: password.c

get_password_fail(PK11SlotInfo *slot UNUSED,
		  PRBool retry UNUSED,
		  void *arg UNUSED)
{
	return NULL;
}

Project: pesign
File: cms_common.c

generate_ava(cms_context *cms, SECItem *der, CERTAVA *certava)
{
	ava ava;

	SECOidData *oid;

	void *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
	if (arena == NULL)
		cmsreterr(-1, cms, "could not create arena");

	void *real_arena = cms->arena;
	cms->arena = arena;

	oid = SECOID_FindOID(&certava->type);
	if (!oid) {
		save_port_err() {
			PORT_FreeArena(arena, PR_TRUE);
		}
		cms->arena = real_arena;
		cmsreterr(-1, cms, "could not find OID");
	}

	int rc = generate_object_id(cms, &ava.type, oid->offset);
	if (rc < 0) {
		PORT_FreeArena(arena, PR_TRUE);
		cms->arena = real_arena;
		return -1;
	}

	memcpy(&ava.value, &certava->value, sizeof (ava.value));

	void *ret;
	SECItem tmp;
	ret = SEC_ASN1EncodeItem(arena, &tmp, &ava, AVATemplate);
	if (ret == NULL) {
		save_port_err() {
			PORT_FreeArena(arena, PR_TRUE);
		}
		cms->arena = real_arena;
		cmsreterr(-1, cms, "could not encode AVA");
	}

	der->type = tmp.type;
	der->len = tmp.len;
	der->data = PORT_ArenaAlloc(real_arena, tmp.len);
	if (!der->data) {
		save_port_err() {
			PORT_FreeArena(arena, PR_TRUE);
		}
		cms->arena = real_arena;
		cmsreterr(-1, cms, "could not allocate AVA");
	}
	memcpy(der->data, tmp.data, tmp.len);
	PORT_FreeArena(arena, PR_TRUE);
	cms->arena = real_arena;

	return 0;
}

Project: pesign
File: cms_common.c

generate_auth_info(cms_context *cms, SECItem *der, char *url)
{
	AuthInfo ai;

	SECOidData *oid = SECOID_FindOIDByTag(SEC_OID_PKIX_CA_ISSUERS);
	if (!oid)
		cmsreterr(-1, cms, "could not get CA issuers OID");

	memcpy(&ai.oid, &oid->oid, sizeof (ai.oid));

	SECItem urlitem = {
		.data = (unsigned char *)url,
		.len = strlen(url),
		.type = siBuffer
	};
	int rc = make_context_specific(cms, 6, &ai.url, &urlitem);
	if (rc < 0)
		return rc;

	void *ret;
	SECItem unwrapped;
	ret = SEC_ASN1EncodeItem(cms->arena, &unwrapped, &ai, AuthInfoTemplate);
	if (ret == NULL)
		cmsreterr(-1, cms, "could not encode CA Issuers");

	rc = wrap_in_seq(cms, der, &unwrapped, 1);
	if (rc < 0)
		return rc;
	return 0;

	/* I've no idea how to get SEC_ASN1EncodeItem to spit out the thing
	 * we actually want here.  So once again, just force the data to
	 * look correct :( */
	if (unwrapped.len < 12) {
		cms->log(cms, LOG_ERR, "%s:%s:%d generated CA Issuers Info "
			"cannot possibly be valid",
			__FILE__, __func__, __LINE__);
		return -1;
	}
	unwrapped.data[12] = 0x86;
	unwrapped.type = siBuffer;

	AuthInfo wrapper;
	oid = SECOID_FindOIDByTag(SEC_OID_X509_AUTH_INFO_ACCESS);
	if (!oid)
		cmsreterr(-1, cms, "could not find Auth Info Access OID");

	memcpy(&wrapper.oid, &oid->oid, sizeof (ai.oid));

	wrap_in_seq(cms, &wrapper.url, &unwrapped, 1);

	ret = SEC_ASN1EncodeItem(cms->arena, der, &wrapper,
					AuthInfoWrapperTemplate);
	if (ret == NULL)
		cmsreterr(-1, cms, "could not encode CA Issuers OID");

	return 0;
}

Project: pesign
File: daemon.c

set_up_inpe(context *ctx, int fd, Pe **pe)
{
	*pe = pe_begin(fd, PE_C_READ_MMAP, NULL);
	if (!*pe)
		*pe = pe_begin(fd, PE_C_READ, NULL);
	if (!*pe) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"could not parse PE binary: %s",
			pe_errmsg(pe_errno()));
		return -1;
	}

	int rc = parse_signatures(&ctx->cms->signatures,
				  &ctx->cms->num_signatures, *pe);
	if (rc < 0) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"could not parse signature list");
		pe_end(*pe);
		*pe = NULL;
		return -1;
	}
	return 0;
}

Project: pesign
File: cms_common.c

find_named_certificate(cms_context *cms, char *name, CERTCertificate **cert)
{
	if (!name) {
		cms->log(cms, LOG_ERR, "no certificate name specified");
		return -1;
	}

	PK11_SetPasswordFunc(cms->func ? cms->func : SECU_GetModulePassword);

	PK11SlotList *slots = NULL;
	slots = PK11_GetAllTokens(CKM_RSA_PKCS, PR_FALSE, PR_TRUE, cms);
	if (!slots)
		cmsreterr(-1, cms, "could not get pk11 token list");

	PK11SlotListElement *psle = NULL;
	psle = PK11_GetFirstSafe(slots);
	if (!psle) {
		save_port_err() {
			PK11_FreeSlotList(slots);
		}
		cmsreterr(-1, cms, "could not get pk11 safe");
	}

	while (psle) {
		if (!strcmp(cms->tokenname, PK11_GetTokenName(psle->slot)))
			break;

		psle = PK11_GetNextSafe(slots, psle, PR_FALSE);
	}

	if (!psle) {
		save_port_err() {
			PK11_FreeSlotList(slots);
			cms->log(cms, LOG_ERR, "could not find token \"%s\"",
				 cms->tokenname);
		}
		return -1;
	}

	SECStatus status;
	if (PK11_NeedLogin(psle->slot) && !PK11_IsLoggedIn(psle->slot, cms)) {
		status = PK11_Authenticate(psle->slot, PR_TRUE, cms);
		if (status != SECSuccess) {
			save_port_err() {
				PK11_DestroySlotListElement(slots, &psle);
				PK11_FreeSlotList(slots);
				cms->log(cms, LOG_ERR,
					 "authentication failed for token \"%s\"",
					 cms->tokenname);
			}
			return -1;
		}
	}

	CERTCertList *certlist = NULL;
	certlist = PK11_ListCertsInSlot(psle->slot);
	if (!certlist) {
		save_port_err() {
			PK11_DestroySlotListElement(slots, &psle);
			PK11_FreeSlotList(slots);
		}
		cmsreterr(-1, cms, "could not get certificate list");
	}

	CERTCertListNode *node = NULL;
	for_each_cert(certlist, tmpnode) {
		if (!strcmp(tmpnode->cert->subjectName, name)) {
			node = tmpnode;
			break;
		}
	}
	/* If we're looking up the issuer of some cert, and the issuer isn't
	 * in the database, we'll get back what is essentially a template
	 * that's in NSS's cache waiting to be filled out.  We can't use that,
	 * it'll just cause CERT_DupCertificate() to segfault. */
	if (!node || !node->cert || !node->cert->derCert.data
	    || !node->cert->derCert.len
	    || !node->cert->derIssuer.data
	    || !node->cert->derIssuer.len) {
		PK11_DestroySlotListElement(slots, &psle);
		PK11_FreeSlotList(slots);
		CERT_DestroyCertList(certlist);

		return -1;
	}

	*cert = CERT_DupCertificate(node->cert);

	PK11_DestroySlotListElement(slots, &psle);
	PK11_FreeSlotList(slots);
	CERT_DestroyCertList(certlist);

	return 0;
}

Project: pesign
File: daemon.c

handle_signing(context *ctx, struct pollfd *pollfd, socklen_t size,
	       int attached, bool with_file_type)
{
	struct msghdr msg;
	struct iovec iov;
	ssize_t n;
	char *buffer = malloc(size);
	uint32_t file_format;

	if (!buffer) {
oom:
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"unable to allocate memory: %m");
		exit(1);
	}

	memset(&msg, '\0', sizeof(msg));

	iov.iov_base = buffer;
	iov.iov_len = size;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	n = recvmsg(pollfd->fd, &msg, MSG_WAITALL);

	if (with_file_type) {
		file_format = *((uint32_t *) buffer);
		n -= sizeof(uint32_t);
	} else {
		file_format = FORMAT_PE_BINARY;
	}

	pesignd_string *tn = (pesignd_string *)(buffer + sizeof(uint32_t));
	if (n < (long long)sizeof(tn->size)) {
malformed:
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"handle_signing: invalid data");
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"possible exploit attempt. closing.");
		close(pollfd->fd);
		return;
	}

	n -= sizeof(tn->size);
	if ((size_t)n < tn->size)
		goto malformed;
	n -= tn->size;

	/* authenticating with nss frees these ... best API ever. */
	ctx->cms->tokenname = PORT_ArenaStrdup(ctx->cms->arena,
						(char *)tn->value);
	if (!ctx->cms->tokenname)
		goto oom;

	if ((size_t)n < sizeof(tn->size))
		goto malformed;
	pesignd_string *cn = pesignd_string_next(tn);
	n -= sizeof(cn->size);
	if ((size_t)n < cn->size)
		goto malformed;

	ctx->cms->certname = PORT_ArenaStrdup(ctx->cms->arena,
						(char *)cn->value);
	if (!ctx->cms->certname)
		goto oom;

	n -= cn->size;
	if (n != 0)
		goto malformed;

	int infd=-1;
	socket_get_fd(ctx, pollfd->fd, &infd);

	int outfd=-1;
	socket_get_fd(ctx, pollfd->fd, &outfd);

	ctx->cms->log(ctx->cms, ctx->priority|LOG_NOTICE,
		"attempting to sign with key \"%s:%s\"",
		tn->value, cn->value);
	free(buffer);

	int rc = find_certificate(ctx->cms, 1);
	if (rc < 0) {
		goto finish;
	}

	switch (file_format) {
	case FORMAT_PE_BINARY:
		rc = sign_pe(ctx, infd, outfd, attached);
		break;
	case FORMAT_KERNEL_MODULE:
		rc = sign_kmod(ctx, infd, outfd, attached);
		break;
	default:
		rc = -1;
		break;
	}

	if (rc < 0)
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			      "unrecognised format %d", file_format);

finish:
	close(infd);
	close(outfd);

	send_response(ctx, ctx->cms, pollfd, rc);
	teardown_digests(ctx->cms);
}

Project: pesign
File: daemon.c

handle_get_cmd_version(context *ctx, struct pollfd *pollfd, socklen_t size)
{
	struct msghdr msg;
	struct iovec iov;
	ssize_t n;

	int rc = cms_context_alloc(&ctx->cms);
	if (rc < 0) {
		send_response(ctx, ctx->backup_cms, pollfd, rc);
		return;
	}

	steal_from_cms(ctx->backup_cms, ctx->cms);

	char *buffer = malloc(size);
	if (!buffer) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"unable to allocate memory: %m");
		exit(1);
	}

	memset(&msg, '\0', sizeof(msg));

	iov.iov_base = buffer;
	iov.iov_len = size;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	n = recvmsg(pollfd->fd, &msg, MSG_WAITALL);

	int32_t version = -1;
	uint32_t command;

	if (n < (long long)sizeof(command)) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"unlock-token: invalid data");
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"possible exploit attempt. closing.");
		close(pollfd->fd);
		return;
	}

	memcpy(&command, buffer, sizeof (command));
	ctx->cms->log(ctx->cms, ctx->priority|LOG_NOTICE,
			"searching for command %d", command);

	for (int i = 0; cmd_table[i].cmd != CMD_LIST_END; i++) {
		if (cmd_table[i].cmd == command) {
			ctx->cms->log(ctx->cms, ctx->priority|LOG_NOTICE,
					"cmd-version: found command \"%s\" "
					"version %d",
					cmd_table[i].name,
					cmd_table[i].version);
			version = cmd_table[i].version;
			break;
		}
	}

	if (version == -1) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_NOTICE,
				"cmd-version: could not find command %d",
				command);
	}
	send_response(ctx, ctx->cms, pollfd, version);

	free(buffer);

	hide_stolen_goods_from_cms(ctx->cms, ctx->backup_cms);
	cms_context_fini(ctx->cms);
}

Project: pesign
File: cms_common.c

generate_keys(cms_context *cms, PK11SlotInfo *slot,
		SECKEYPrivateKey **privkey, SECKEYPublicKey **pubkey)
{
	PK11RSAGenParams rsaparams = {
		.keySizeInBits = 2048,
		.pe = 0x010001,
	};

	SECStatus rv;
	rv = PK11_Authenticate(slot, PR_TRUE, cms);
	if (rv != SECSuccess)
		cmsreterr(-1, cms, "could not authenticate with pk11 service");

	void *params = &rsaparams;
	*privkey = PK11_GenerateKeyPair(slot, CKM_RSA_PKCS_KEY_PAIR_GEN,
					params, pubkey, PR_TRUE, PR_TRUE,
					cms);
	if (!*privkey)
		cmsreterr(-1, cms, "could not generate RSA keypair");
	return 0;
}

Project: pesign
File: daemon.c

do_shutdown(context *ctx, int nsockets, struct pollfd *pollfds)
{
	unlink(SOCKPATH);
	unlink(PIDFILE);

	for (int i = 0; i < ctx->ntokennames; i++)
		free(ctx->tokennames[i]);
	if (ctx->tokennames)
		free(ctx->tokennames);
	ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_NOTICE,
			"pesignd exiting (pid %d)", getpid());

	xfree(ctx->errstr);

	for (int i = 0; i < nsockets; i++)
		close(pollfds[i].fd);
	free(pollfds);
}

Project: pesign
File: daemon.c

daemonize(cms_context *cms_ctx, char *certdir, int do_fork)
{
	int rc = 0;
	context ctx = {
		.backup_cms = cms_ctx,
		.priority = do_fork ? LOG_PID
				    : LOG_PID|LOG_PERROR,
	};

	ctx.backup_cms = cms_ctx;
	ctx.backup_cms->log_priv = &ctx;
	ctx.sd = -1;

	if (getuid() != 0) {
		fprintf(stderr, "pesignd must be started as root");
		exit(1);
	}

	check_socket(&ctx);

	openlog("pesignd", LOG_PID, LOG_DAEMON);

	if (do_fork) {
		pid_t pid;

		if ((pid = fork())) {
			sleep(2);
			return 0;
		}
	}
	ctx.pid = getpid();
	write_pid_file(ctx.pid);
	ctx.backup_cms->log(ctx.backup_cms, ctx.priority|LOG_NOTICE,
		"pesignd starting (pid %d)", ctx.pid);
	daemon_logger(ctx.backup_cms, ctx.priority|LOG_NOTICE,
		"pesignd starting (pid %d)", ctx.pid);

	SECStatus status = NSS_Init(certdir);
	int error = errno;
	if (status != SECSuccess) {
		char *globpattern = NULL;
		rc = asprintf(&globpattern, "%s/cert*.db",
			      certdir);
		if (rc > 0) {
			glob_t globbuf;
			memset(&globbuf, 0, sizeof(globbuf));
			rc = glob(globpattern, GLOB_ERR, NULL,
				  &globbuf);
			if (rc != 0) {
				errno = error;
				ctx.backup_cms->log(ctx.backup_cms,
					ctx.priority|LOG_NOTICE,
					"Could not open NSS database (\"%s\"): %m",
					PORT_ErrorToString(PORT_GetError()));
				exit(1);
			}
		}
	}
	if (status != SECSuccess) {
		errno = error;
		ctx.backup_cms->log(ctx.backup_cms, ctx.priority|LOG_NOTICE,
				    "Could not initialize nss.\n"
				    "NSS says \"%s\" errno says \"%m\"\n",
				    PORT_ErrorToString(PORT_GetError()));
		exit(1);
	}

	status = register_oids(ctx.backup_cms);
	if (status != SECSuccess) {
		ctx.backup_cms->log(ctx.backup_cms, ctx.priority|LOG_NOTICE,
			"Could not register OIDs\n");
		exit(1);
	}

	if (do_fork) {
		int fd = open("/dev/zero", O_RDONLY);
		if (fd < 0) {
			ctx.backup_cms->log(ctx.backup_cms,
					ctx.priority|LOG_ERR,
					"could not open /dev/zero: %m");
			exit(1);
		}
		close(STDIN_FILENO);
		rc = dup2(fd, STDIN_FILENO);
		if (rc < 0) {
			ctx.backup_cms->log(ctx.backup_cms,
				ctx.priority|LOG_ERR,
				"could not set up standard input: %m");
			exit(1);
		}
		close(fd);

		fd = open("/dev/null", O_WRONLY);
		if (fd < 0) {
			ctx.backup_cms->log(ctx.backup_cms,
					ctx.priority|LOG_ERR,
					"could not open /dev/null: %m");
			exit(1);
		}
		close(STDOUT_FILENO);
		rc = dup2(fd, STDOUT_FILENO);
		if (rc < 0) {
			ctx.backup_cms->log(ctx.backup_cms,
				ctx.priority|LOG_ERR,
				"could not set up standard output: %m");
			exit(1);
		}

		close(STDERR_FILENO);
		rc = dup2(fd, STDERR_FILENO);
		if (rc < 0) {
			ctx.backup_cms->log(ctx.backup_cms,
				ctx.priority|LOG_ERR,
				"could not set up standard error: %m");
			exit(1);
		}
		close(fd);
	}

	prctl(PR_SET_NAME, "pesignd", 0, 0, 0);

	setsid();

	if (do_fork) {
		struct sigaction sa = {
			.sa_handler = quit_handler,
		};
		sigaction(SIGQUIT, &sa, NULL);
		sigaction(SIGINT, &sa, NULL);
		sigaction(SIGTERM, &sa, NULL);
	}

	char *homedir = NULL;

	rc = get_uid_and_gid(&ctx, &homedir);
	if (rc < 0) {
		ctx.backup_cms->log(ctx.backup_cms, ctx.priority|LOG_ERR,
			"could not get group and user information "
			"for pesign: %m");
		exit(1);
	}

	chdir(homedir ? homedir : "/");

	if (getuid() == 0) {
		/* process is running as root, drop privileges */
		if (setgid(ctx.gid) != 0 || setgroups(0, NULL)) {
			ctx.backup_cms->log(ctx.backup_cms,
				ctx.priority|LOG_ERR,
				"unable to drop group privileges: %m");
			exit(1);
		}
		if (setuid(ctx.uid) != 0) {
			ctx.backup_cms->log(ctx.backup_cms,
				ctx.priority|LOG_ERR,
				"unable to drop user privileges: %m");
			exit(1);
		}
	}

	set_up_socket(&ctx);

	cms_set_pw_callback(ctx.backup_cms, get_password_fail);
	cms_set_pw_data(ctx.backup_cms, NULL);
	if (do_fork)
		ctx.backup_cms->log = daemon_logger;

	rc = handle_events(&ctx);

	status = NSS_Shutdown();
	if (status != SECSuccess) {
		ctx.backup_cms->log(ctx.backup_cms, ctx.priority|LOG_ERR,
			"NSS_Shutdown failed: %s\n",
			PORT_ErrorToString(PORT_GetError()));
		exit(1);
	}
	return rc;
}

Project: pesign
File: password.c

get_env(const char *name)
{
	char *value;

	value = secure_getenv(name);
	if (value)
		value = strdup(value);
	return value;
}

Project: pesign
File: password.c

read_password(FILE *in, FILE *out, char *buf, size_t bufsz)
{
	int infd = fileno(in);
	struct termios tio;
	char *ret;

	ret = fgets(buf, bufsz, in);

	if (isatty(infd)) {
		fprintf(out, "\n");
		fflush(out);

		tcgetattr(infd, &tio);
		tio.c_lflag |= ECHO;
		tcsetattr(infd, TCSAFLUSH, &tio);
	}
	if (ret == NULL)
		return -1;

	buf[strlen(buf)-1] = '\0';
	return 0;
}

Project: pesign
File: pesign.c

main(int argc, char *argv[])
{
	int rc;

	pesign_context *ctxp;
	file_format fmt = FORMAT_PE_BINARY;

	int list = 0;
	int remove = 0;
	int daemon = 0;
	int fork = 1;
	int padding = 1;
	int need_db = 0;
	int check_vendor_cert = 1;

	char *digest_name = "sha256";
	char *tokenname = "NSS Certificate DB";
	char *origtoken = tokenname;
	char *certname = NULL;
	char *certdir = "/etc/pki/pesign";
	char *signum = NULL;

	secuPWData pwdata;

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

	setenv("NSS_DEFAULT_DB_TYPE", "sql", 0);

	rc = pesign_context_new(&ctxp);
	if (rc < 0) {
		fprintf(stderr, "Could not initialize context: %m\n");
		exit(1);
	}
	verbose = &ctxp->verbose;

	poptContext optCon;
	struct poptOption options[] = {
		{.argInfo = POPT_ARG_INTL_DOMAIN,
		 .arg = "pesign" },
		{.longName = "in",
		 .shortName = 'i',
		 .argInfo = POPT_ARG_STRING,
		 .arg = &ctxp->infile,
		 .descrip = "specify input file",
		 .argDescrip = "<infile>"},
		{.longName = "out",
		 .shortName = 'o',
		 .argInfo = POPT_ARG_STRING,
		 .arg = &ctxp->outfile,
		 .descrip = "specify output file",
		 .argDescrip = "<outfile>" },
		{.longName = "certificate",
		 .shortName = 'c',
		 .argInfo = POPT_ARG_STRING,
		 .arg = &certname,
		 .descrip = "specify certificate nickname",
		 .argDescrip = "<certificate nickname>" },
		{.longName = "certdir",
		 .shortName = 'n',
		 .argInfo = POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT,
		 .arg = &certdir,
		 .descrip = "specify nss certificate database directory",
		 .argDescrip = "<certificate directory path>" },
		{.longName = "force",
		 .shortName = 'f',
		 .argInfo = POPT_ARG_VAL,
		 .arg = &ctxp->force,
		 .val = 1,
		 .descrip = "force overwriting of output file" },
		{.longName = "sign",
		 .shortName = 's',
		 .argInfo = POPT_ARG_VAL,
		 .arg = &ctxp->sign,
		 .val = 1,
		 .descrip = "create a new signature" },
		{.longName = "hash",
		 .shortName = 'h',
		 .argInfo = POPT_ARG_VAL,
		 .arg = &ctxp->hash,
		 .val = 1,
		 .descrip = "hash binary" },
		{.longName = "digest_type",
		 .shortName = 'd',
		 .argInfo = POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT,
		 .arg = &digest_name,
		 .descrip = "digest type to use for pe hash" },
		{.longName = "import-signed-certificate",
		 .shortName = 'm',
		 .argInfo = POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN,
		 .arg = &ctxp->insig,
		 .descrip = "<insig>" },
		{.longName = "export-signed-attributes",
		 .shortName = 'E',
		 .argInfo = POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN,
		 .arg = &ctxp->outsattrs,
		 .descrip = "export signed attributes to file",
		 .argDescrip = "<signed_attributes_file>" },
		{.longName = "import-signed-attributes",
		 .shortName = 'I',
		 .argInfo = POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN,
		 .arg = &ctxp->insattrs,
		 .descrip = "import signed attributes from file",
		 .argDescrip = "<signed_attributes_file>" },
		{.longName = "import-raw-signature",
		 .shortName = 'R',
		 .argInfo = POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN,
		 .arg = &ctxp->rawsig,
		 .descrip = "import raw signature from file",
		 .argDescrip = "<inraw>" },
		{.longName = "signature-number",
		 .shortName = 'u',
		 .argInfo = POPT_ARG_STRING,
		 .arg = &signum,
		 .descrip = "specify which signature to operate on","<sig-number>"},
		{.longName = "list-signatures",
		 .shortName = 'l',
		 .argInfo = POPT_ARG_VAL|POPT_ARGFLAG_DOC_HIDDEN,
		 .arg = &list,
		 .val = 1,
		 .descrip = "list signatures" },
		{.longName = "nss-token",
		 .shortName = 't',
		 .argInfo = POPT_ARG_STRING|POPT_ARGFLAG_SHOW_DEFAULT,
		 .arg = &tokenname,
		 .descrip = "NSS token holding signing key" },
		{.longName = "show-signature",
		 .shortName = 'S',
		 .argInfo = POPT_ARG_VAL,
		 .arg = &list,
		 .val = 1,
		 .descrip = "show signature" },
		{.longName = "remove-signature",
		 .shortName = 'r',
		 .argInfo = POPT_ARG_VAL,
		 .arg = &remove,
		 .val = 1,
		 .descrip = "remove signature" },
		{.longName = "export-signature",
		 .shortName = 'e',
		 .argInfo = POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN,
		 .arg = &ctxp->outsig,
		 .descrip = "export signature to file",
		 .argDescrip = "<outsig>" },
		{.longName = "export-pubkey",
		 .shortName = 'K',
		 .argInfo = POPT_ARG_STRING,
		 .arg = &ctxp->outkey,
		 .descrip = "export pubkey to file",
		 .argDescrip = "<outkey>" },
		{.longName = "export-cert",
		 .shortName = 'C',
		 .argInfo = POPT_ARG_STRING,
		 .arg = &ctxp->outcert,
		 .descrip = "export signing cert to file",
		 .argDescrip = "<outcert>" },
		{.longName = "ascii-armor",
		 .shortName = 'a',
		 .argInfo = POPT_ARG_VAL,
		 .arg = &ctxp->ascii,
		 .val = 1,
		 .descrip = "use ascii armoring" },
		{.longName = "daemonize",
		 .shortName = 'D',
		 .argInfo = POPT_ARG_VAL,
		 .arg = &daemon,
		 .val = 1,
		 .descrip = "run as a daemon process" },
		{.longName = "nofork",
		 .shortName = 'N',
		 .argInfo = POPT_ARG_VAL,
		 .arg = &fork,
		 .descrip = "don't fork when daemonizing" },
		{.longName = "verbose",
		 .shortName = 'v',
		 .argInfo = POPT_ARG_VAL|POPT_ARG_LONG|POPT_ARGFLAG_OPTIONAL,
		 .arg = &ctxp->verbose,
		 .val = 1,
		 .descrip = "be more verbose" },
		{.longName = "debug",
		 .shortName = '\0',
		 .argInfo = POPT_ARG_VAL|POPT_ARG_LONG|POPT_ARGFLAG_OPTIONAL,
		 .arg = &ctxp->verbose,
		 .val = 2,
		 .descrip = "be very verbose" },

		{.longName = "padding",
		 .shortName = 'P',
		 .argInfo = POPT_ARG_VAL,
		 .arg = &padding,
		 .val = 1,
		 .descrip = "pad data section (default)" },
		{.longName = "nopadding",
		 .shortName = 'p',
		 .argInfo = POPT_ARG_VAL,
		 .arg = &padding,
		 .val = 0,
		 .descrip = "do not pad the data section" },
		{.longName = "no-vendor-cert",
		 .shortName = 'V',
		 .argInfo = POPT_ARG_VAL,
		 .arg = &check_vendor_cert,
		 .val = 0,
		 .descrip = "do not hash the .vendor_cert section." },
		{.longName = "pwfile",
		 .shortName = '\0',
		 .argInfo = POPT_ARG_STRING|POPT_ARGFLAG_DOC_HIDDEN,
		 .arg = &pwdata.data,
		 .descrip = "file to read passwords from.",
		 .argDescrip = "<pwfile>" },
		POPT_AUTOALIAS
		POPT_AUTOHELP
		POPT_TABLEEND
	};

	optCon = poptGetContext("pesign", argc, (const char **)argv, options,0);

	rc = poptReadDefaultConfig(optCon, 0);
	if (rc < 0 && !(rc == POPT_ERROR_ERRNO && errno == ENOENT)) {
		fprintf(stderr, "pesign: poptReadDefaultConfig failed: %s\n",
		poptStrerror(rc));
		exit(1);
	}

	while ((rc = poptGetNextOpt(optCon)) > 0) {
		switch (rc) {
		case POPT_RET_PWDB:
			dprintf("POPT_RET_PWDB:\"%s\"", pwdata.data ? pwdata.data : "(null)");
			if (pwdata.source != PW_SOURCE_INVALID)
				errx(1, "only one password/pin method can be used at a time");
			if (pwdata.data == NULL)
				errx(1, "--pwfile requires a file name as an argument");
			pwdata.source = PW_FROMFILEDB;
			pwdata.data = strdup(pwdata.data);
			if (!pwdata.data)
				err(1, "could not allocate memory");
			continue;
		}
	}

	dprintf("pwdata.source:%d %schecking for PESIGN_TOKEN_PIN",
		pwdata.source,
		pwdata.source == PW_SOURCE_INVALID ? "" : "not ");
	if (pwdata.source == PW_SOURCE_INVALID && secure_getenv("PESIGN_TOKEN_PIN")) {
		pwdata.source = PW_FROMENV;
		pwdata.data = strdup(secure_getenv("PESIGN_TOKEN_PIN"));
		if (!pwdata.data)
			err(1, "could not allocate memory");
	}
	pwdata.orig_source = pwdata.source;

	if (rc < -1) {
		fprintf(stderr, "pesign: Invalid argument: %s: %s\n",
			poptBadOption(optCon, 0), poptStrerror(rc));
		exit(1);
	}

	if (poptPeekArg(optCon)) {
		fprintf(stderr, "pesign: Invalid Argument: \"%s\"\n",
				poptPeekArg(optCon));
		exit(1);
	}

	poptFreeContext(optCon);

	if (signum) {
		errno = 0;
		ctxp->signum = strtol(signum, NULL, 0);
		if (errno != 0) {
			fprintf(stderr, "invalid signature number: %m\n");
			exit(1);
		}
	}

	int action = 0;
	if (daemon)
		action |= DAEMONIZE;

	if (ctxp->rawsig) {
		action |= IMPORT_RAW_SIGNATURE;
		need_db = 1;
	}

	if (ctxp->insattrs)
		action |= IMPORT_SATTRS;

	if (ctxp->outsattrs)
		action |= EXPORT_SATTRS;

	if (ctxp->insig)
		action |= IMPORT_SIGNATURE;

	if (ctxp->outkey) {
		action |= EXPORT_PUBKEY;
		need_db = 1;
	}

	if (ctxp->outcert) {
		action |= EXPORT_CERT;
		need_db = 1;
	}

	if (ctxp->outsig)
		action |= EXPORT_SIGNATURE;

	if (remove != 0)
		action |= REMOVE_SIGNATURE;

	if (list != 0)
		action |= LIST_SIGNATURES;

	if (ctxp->sign) {
		action |= GENERATE_SIGNATURE;
		if (!(action & EXPORT_SIGNATURE))
			action |= IMPORT_SIGNATURE;
		need_db = 1;
	}

	if (ctxp->hash)
		action |= GENERATE_DIGEST|PRINT_DIGEST;

	if (!check_vendor_cert) {
		action |= OMIT_VENDOR_CERT;
	}

	if (!daemon) {
		SECStatus status;
		int error = 0;
		if (need_db) {
			status = NSS_Init(certdir);
			if (status != SECSuccess) {
				char *globpattern = NULL;
				error = errno;
				rc = asprintf(&globpattern, "%s/cert*.db",
					      certdir);
				if (rc > 0) {
					glob_t globbuf;
					memset(&globbuf, 0, sizeof(globbuf));
					rc = glob(globpattern, GLOB_ERR, NULL,
						  &globbuf);
					if (rc != 0) {
						err(1, "Could not open NSS database (\"%s\")",
						     PORT_ErrorToString(PORT_GetError()));
					}
				}
			}
		} else
			status = NSS_NoDB_Init(NULL);
		if (status != SECSuccess) {
			errno = error;
			errx(1, "Could not initialize nss.\n"
			        "NSS says \"%s\" errno says \"%m\"\n",
			     PORT_ErrorToString(PORT_GetError()));
		}

		status = register_oids(ctxp->cms_ctx);
		if (status != SECSuccess) {
			fprintf(stderr, "Could not register OIDs\n");
			exit(1);
		}
	}

	rc = set_digest_parameters(ctxp->cms_ctx, digest_name);
	int is_help  = strcmp(digest_name, "help") ? 0 : 1;
	if (rc < 0) {
		if (!is_help) {
			fprintf(stderr, "Digest \"%s\" not found.\n",
				digest_name);
		}
		exit(!is_help);
	}

	if (pwdata.source != PW_DEVICE)
		cms_set_pw_data(ctxp->cms_ctx, &pwdata);

	ctxp->cms_ctx->omit_vendor_cert = !check_vendor_cert;

	ctxp->cms_ctx->tokenname = tokenname ?
		PORT_ArenaStrdup(ctxp->cms_ctx->arena, tokenname) : NULL;
	if (tokenname && !ctxp->cms_ctx->tokenname) {
		fprintf(stderr, "could not allocate token name: %s\n",
			PORT_ErrorToString(PORT_GetError()));
		exit(1);
	}
	if (tokenname != origtoken)
		free(tokenname);

	ctxp->cms_ctx->certname = certname ?
		PORT_ArenaStrdup(ctxp->cms_ctx->arena, certname) : NULL;
	if (certname && !ctxp->cms_ctx->certname) {
		fprintf(stderr, "could not allocate certificate name: %s\n",
			PORT_ErrorToString(PORT_GetError()));
		exit(1);
	}
	if (certname)
		free(certname);


	if (ctxp->sign) {
		if (!ctxp->cms_ctx->certname) {
			fprintf(stderr, "pesign: signing requested but no "
				"certificate nickname provided\n");
			exit(1);
		}
	}

	if (ctxp->infile) {
		char *ext = strrchr(ctxp->infile, '.');
		if (ext && strcmp(ext, ".ko") == 0)
			fmt = FORMAT_KERNEL_MODULE;
	}

	switch (action) {
		case NO_FLAGS:
			fprintf(stderr, "pesign: Nothing to do.\n");
			exit(0);
			break;
		case DAEMONIZE:
			rc = daemonize(ctxp->cms_ctx, certdir, fork);
			break;
		default:
			switch (fmt) {
				case FORMAT_PE_BINARY:
					pe_handle_action(ctxp, action, padding);
					break;
				case FORMAT_KERNEL_MODULE:
					kmod_handle_action(ctxp, action);
					break;
			}
	}
	pesign_context_free(ctxp);

	if (!daemon) {
		SECStatus status = NSS_Shutdown();
		if (status != SECSuccess) {
			fprintf(stderr, "could not shut down NSS: %s",
				PORT_ErrorToString(PORT_GetError()));
			exit(1);
		}
	}

	return (rc < 0);
}

Project: pesign
File: password.c

can_prompt_again(secuPWData *pwdata)
{
	if (pwdata->orig_source == PW_PROMPT)
		return true;

	if (pwdata->source == PW_DEVICE)
		return true;

	return false;
}

Project: pesign
File: cms_common.c

generate_string(cms_context *cms, SECItem *der, char *str)
{
	SECItem input;

	input.data = (void *)str;
	input.len = strlen(str);
	input.type = siBMPString;

	void *ret;
	ret = SEC_ASN1EncodeItem(cms->arena, der, &input,
						SEC_PrintableStringTemplate);
	if (ret == NULL)
		cmsreterr(-1, cms, "could not encode string");
	return 0;
}

Project: pesign
File: password.c

SECU_GetModulePassword(PK11SlotInfo *slot, PRBool retry, void *arg)
{
	char *prompt = NULL;
	cms_context *cms = (cms_context *)arg;
	secuPWData *pwdata;
	secuPWData pwxtrn = { .source = PW_DEVICE, .orig_source = PW_DEVICE, .data = NULL };
	char *pw;
	int rc;

	ingress();

	if (PK11_ProtectedAuthenticationPath(slot)) {
		dprintf("prompting for PW_DEVICE data");
		pwdata = &pwxtrn;
	} else {
		dprintf("using pwdata from cms");
		pwdata = &cms->pwdata;
	}

	if (pwdata->source <= PW_SOURCE_INVALID ||
	    pwdata->source >= PW_SOURCE_MAX ||
	    pwdata->orig_source <= PW_SOURCE_INVALID ||
	    pwdata->orig_source >= PW_SOURCE_MAX) {
		dprintf("pwdata is invalid");
		return NULL;
	}

	dprintf("pwdata:%p retry:%d", pwdata, retry);
	dprintf("pwdata->source:%s (%d) orig:%s (%d)",
		pw_source_names[pwdata->source], pwdata->source,
		pw_source_names[pwdata->orig_source], pwdata->orig_source);
	dprintf("pwdata->data:%p (\"%s\")", pwdata->data,
		pwdata->data ? pwdata->data : "(null)");

	if (retry) {
		warnx("Incorrect password/PIN entered.");
		if (!can_prompt_again(pwdata)) {
			egress();
			return NULL;
		}
	}

	switch (pwdata->source) {
	case PW_PROMPT:
		rc = asprintf(&prompt, "Enter Password or Pin for \"%s\":",
			      PK11_GetTokenName(slot));
		if (rc < 0)
			return NULL;
		pw = SECU_GetPasswordString(NULL, prompt);
		if (!pw)
			return NULL;
		free(prompt);

		pwdata->source = PW_PLAINTEXT;
		egress();
		return pw;

	case PW_DEVICE:
		dprintf("pwdata->source:PW_DEVICE");
		rc = asprintf(&prompt,
			      "Press Enter, then enter PIN for \"%s\" on external device.\n",
			      PK11_GetTokenName(slot));
		if (rc < 0)
			return NULL;
		pw = SECU_GetPasswordString(NULL, prompt);
		free(prompt);
		return pw;

	case PW_FROMFILEDB:
	case PW_DATABASE:
		dprintf("pwdata->source:%s", pw_source_names[pwdata->source]);
		/* Instead of opening and closing the file every time, get the pw
		 * once, then keep it in memory (duh).
		 */
		pw = SECU_FilePasswd(slot, retry, cms);
		if (pw != NULL) {
			pwdata->source = PW_PLAINTEXT;
			pwdata->data = strdup(pw);
		}
		/* it's already been dup'ed */
		egress();
		return pw;

	case PW_FROMENV:
		dprintf("pwdata->source:PW_FROMENV");
		if (!pwdata || !pwdata->data)
			break;
		pw = get_env(pwdata->data);
		dprintf("env:%s pw:%s", pwdata->data, pw ? pw : "(null)");
		pwdata->data = pw;
		pwdata->source = PW_PLAINTEXT;
		goto PW_PLAINTEXT;

	PW_PLAINTEXT:
	case PW_PLAINTEXT:
		egress();
		if (pwdata && pwdata->data)
			return strdup(pwdata->data);
		return NULL;

	default:
		break;
	}

	warnx("Password check failed: No password found.");
	egress();
	return NULL;
}

Project: pesign
File: cms_common.c

cms_context_init(cms_context *cms)
{
	memset(cms, '\0', sizeof (*cms));

	cms->log = cms_common_log;

	cms->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
	if (!cms->arena)
		cmsreterr(-1, cms, "could not create cryptographic arena");

	cms->selected_digest = -1;

	return 0;
}

Project: pesign
File: password.c

get_password_passthrough(PK11SlotInfo *slot UNUSED,
			 PRBool retry, void *arg)
{
	if (retry || !arg)
		return NULL;

	char *ret = strdup(arg);
	if (!ret)
		err(1, "Could not allocate memory");

	return ret;
}

Project: pesign
File: cms_common.c

is_valid_cert_without_private_key(CERTCertificate *cert, void *data)
{
	struct validity_cbdata *cbd = (struct validity_cbdata *)data;
	PK11SlotInfo *slot = cbd->slot;
	SECKEYPrivateKey *privkey = NULL;

	if (cert == NULL)
		return SECFailure;
	privkey = PK11_FindPrivateKeyFromCert(slot, cert, cbd->cms);
	if (privkey == NULL) {
		cbd->cert = CERT_DupCertificate(cert);
		return SECSuccess;
	}
	return SECFailure;
}

Project: pesign
File: daemon.c

sign_pe(context *ctx, int infd, int outfd, int attached)
{
	Pe *inpe = NULL;

	int rc = set_up_inpe(ctx, infd, &inpe);
	if (rc < 0)
		goto finish;

	if (attached) {
		Pe *outpe = NULL;
		rc = set_up_outpe(ctx, outfd, inpe, &outpe);
		if (rc < 0)
			goto finish;

		rc = generate_digest(ctx->cms, outpe, 1);
		if (rc < 0) {
err_attached:
			pe_end(outpe);
			ftruncate(outfd, 0);
			goto finish;
		}
		ssize_t sigspace = calculate_signature_space(ctx->cms, outpe);
		if (sigspace < 0)
			goto err_attached;
		allocate_signature_space(outpe, sigspace);
		rc = generate_digest(ctx->cms, outpe, 1);
		if (rc < 0)
			goto err_attached;
		rc = generate_signature(ctx->cms);
		if (rc < 0)
			goto err_attached;
		insert_signature(ctx->cms, ctx->cms->num_signatures);
		finalize_signatures(ctx->cms->signatures,
				ctx->cms->num_signatures, outpe);
		pe_end(outpe);
	} else {
		ftruncate(outfd, 0);
		rc = generate_digest(ctx->cms, inpe, 1);
		if (rc < 0) {
err_detached:
			ftruncate(outfd, 0);
			goto finish;
		}
		rc = generate_signature(ctx->cms);
		if (rc < 0)
			goto err_detached;
		rc = export_signature(ctx->cms, outfd, 0);
		if (rc >= 0)
			ftruncate(outfd, rc);
		else if (rc < 0)
			goto err_detached;
	}

finish:
	if (inpe)
		pe_end(inpe);

	return rc;
}

Project: pesign
File: password.c

get_password(FILE *input, FILE *output, char *prompt, PRBool (*ok)(char *))
{
	int infd = fileno(input);
	char phrase[200];
	size_t size = sizeof(phrase);

	ingress();
	memset(phrase, 0, size);

	while(true) {
		int rc;

		print_prompt(input, output, prompt);
		rc = read_password(input, output, phrase, size);
		if (rc < 0)
			return NULL;

		if (!ok)
			break;

		if ((*ok)(phrase))
			break;

		if (!isatty(infd))
			return NULL;
		fprintf(output, "Password does not meet requirements.\n");
		fflush(output);
	}

	egress();
	return (char *)PORT_Strdup(phrase);
}

Project: pesign
File: cms_common.c

wrap_in_seq(cms_context *cms, SECItem *der, SECItem *items, int num_items)
{
	void *ret;

	void *mark = PORT_ArenaMark(cms->arena);

	SEC_ASN1Template tmpl[num_items+2];

	memcpy(&tmpl[0], &SeqTemplateHeader, sizeof(*tmpl));
	tmpl[0].size = sizeof (SECItem) * num_items;

	for (int i = 0; i < num_items; i++) {
		memcpy(&tmpl[i+1], &SeqTemplateTemplate, sizeof(SEC_ASN1Template));
		tmpl[i+1].offset = (i) * sizeof (SECItem);
	}
	memset(&tmpl[num_items + 1], '\0', sizeof(SEC_ASN1Template));

	int rc = 0;
	ret = SEC_ASN1EncodeItem(cms->arena, der, items, tmpl);
	if (ret == NULL) {
		save_port_err() {
			PORT_ArenaRelease(cms->arena, mark);
		}
		cmsreterr(-1, cms, "could not encode set");
	}
	PORT_ArenaUnmark(cms->arena, mark);
	return rc;
}

Project: pesign
File: daemon.c

handle_events(context *ctx)
{
	int rc;
	int nsockets = 1;

	struct pollfd *pollfds = calloc(1, sizeof(struct pollfd));

	if (!pollfds) {
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_ERR,
			"could not allocate memory: %m");
		exit(1);
	}

	pollfds[0].fd = ctx->sd;
	pollfds[0].events = POLLIN|POLLPRI|POLLHUP;

	while (1) {
		if (should_exit != 0) {
shutdown:
			do_shutdown(ctx, nsockets, pollfds);
			return 0;
		}
		rc = ppoll(pollfds, nsockets, NULL, NULL);
		if (should_exit != 0)
			goto shutdown;
		if (rc < 0) {
			ctx->backup_cms->log(ctx->backup_cms,
				ctx->priority|LOG_WARNING,
				"ppoll: %m");
			continue;
		}

		if (pollfds[0].revents & POLLIN) {
			nsockets++;
			struct pollfd *newpollfds = realloc(pollfds,
				nsockets * sizeof(struct pollfd));

			if (!newpollfds) {
				ctx->backup_cms->log(ctx->backup_cms,
					ctx->priority|LOG_ERR,
					"could not allocate memory: %m");
				exit(1);
			}
			pollfds = newpollfds;

			struct sockaddr_un remote;
			socklen_t len = sizeof(remote);
			pollfds[nsockets-1].fd = accept(pollfds[0].fd, &remote,
							&len);
			pollfds[nsockets-1].events = POLLIN|POLLPRI|POLLHUP;
			pollfds[nsockets-1].revents = pollfds[0].revents;
		}
		for (int i = 1; i < nsockets; i++) {
			if (pollfds[i].revents & (POLLHUP|POLLNVAL)) {
				close(pollfds[i].fd);
				if (i == nsockets-1) {
					nsockets--;
					continue;
				}
				for (int j = i; j < nsockets - 1; j++) {
					pollfds[j].fd = pollfds[j+1].fd;
					pollfds[j].events =
						pollfds[j].events;
					pollfds[j].revents =
						pollfds[j].revents;
				}
				nsockets--;
				i--;
				continue;
			}

			if (pollfds[i].revents & (POLLIN|POLLPRI))
				handle_event(ctx, &pollfds[i]);
		}
	}
	return 0;
}

Project: pesign
File: daemon.c

check_socket(context *ctx UNUSED)
{
	errno = 0;
	int rc = access(SOCKPATH, R_OK);
	if (rc == 0) {
		struct sockaddr_un addr_un = {
			.sun_family = AF_UNIX,
			.sun_path = SOCKPATH,
		};

		int sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
		if (sd < 0) {
			fprintf(stderr, "unable to create socket: %m");
			exit(1);
		}

		socklen_t len = strlen(addr_un.sun_path) +
				sizeof(addr_un.sun_family);

		rc = connect(sd, (struct sockaddr *)&addr_un, len);
		if (rc < 0) {
			close(sd);
			unlink(SOCKPATH);
			return;
		}

		struct sockaddr_un remote;
		socklen_t size = sizeof(remote);
		rc = getpeername(sd, &remote, &size);
		if (rc < 0) {
			close(sd);
			return;
		} else {
			fprintf(stderr, "already running");
			exit(1);
		}
	} else {
		/* It could be something other than EEXIST, but it really
		 * doesn't matter since the daemon isn't running.  Blindly
		 * remove it. */
		unlink(SOCKPATH);
	}
}

Project: pesign
File: daemon.c

daemon_logger(cms_context *cms, int priority, char *fmt, ...)
{
	context *ctx = (context *)cms->log_priv;
	va_list ap;
	int rc = 0;

	if (ctx->errstr)
		xfree(ctx->errstr);

	va_start(ap, fmt);
	if (priority & LOG_ERR) {
		va_list aq;

		va_copy(aq, ap);
		rc = vasprintf(&ctx->errstr, fmt, aq);
		va_end(aq);
	}

	vsyslog(ctx->priority | priority, fmt, ap);
	va_end(ap);
	return rc;
}

Project: pesign
File: cms_common.c

generate_validity(cms_context *cms, SECItem *der, time_t start, time_t end)
{
	Validity validity;
	int rc;

	rc = generate_time(cms, &validity.start, start);
	if (rc < 0)
		return rc;

	rc = generate_time(cms, &validity.end, end);
	if (rc < 0)
		return rc;

	void *ret;
	ret = SEC_ASN1EncodeItem(cms->arena, der, &validity, ValidityTemplate);
	if (ret == NULL)
		cmsreterr(-1, cms, "could not encode validity");
	return 0;
}

Project: pesign
File: daemon.c

socket_get_fd(context *ctx, int sd, int *fd)
{
	struct msghdr msg;
	struct iovec iov;
	char buf[2];

	size_t controllen = CMSG_SPACE(sizeof(int));
	struct cmsghdr *cm = malloc(controllen);
	if (!cm) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"unable to allocate memory: %m");
		exit(1);
	}

	memset(&msg, '\0', sizeof(msg));
	iov.iov_base = buf;
	iov.iov_len = 2;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = cm;
	msg.msg_controllen = controllen;

	ssize_t n;
	n = recvmsg(sd, &msg, MSG_WAITALL);
	if (n < 0) {
malformed:
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"unlock-token: invalid data");
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"possible exploit attempt. closing.");
		close(sd);
		return;
	}

	struct cmsghdr *cme = CMSG_FIRSTHDR(&msg);

	if (cme == NULL)
		goto malformed;

	if (cme->cmsg_level != SOL_SOCKET)
		goto malformed;

	if (cme->cmsg_type != SCM_RIGHTS)
		goto malformed;

	*fd = *((int *)CMSG_DATA(cme));

	free(cm);
}

Project: pesign
File: cms_common.c

generate_time(cms_context *cms, SECItem *encoded, time_t when)
{
	static char timebuf[32];
	SECItem whenitem = {.type = SEC_ASN1_UTC_TIME,
			 .data = (unsigned char *)timebuf,
			 .len = 0
	};
	struct tm *tm;

	tm = gmtime(&when);

	whenitem.len = snprintf(timebuf, 32, "%02d%02d%02d%02d%02d%02dZ",
		tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday,
		tm->tm_hour, tm->tm_min, tm->tm_sec);
	if (whenitem.len == 32)
		cmsreterr(-1, cms, "could not encode timestamp");

	if (SEC_ASN1EncodeItem(cms->arena, encoded, &whenitem,
			SEC_UTCTimeTemplate) == NULL)
		cmsreterr(-1, cms, "could not encode timestamp");
	return 0;
}

Project: pesign
File: signer_info.c

sign_blob(cms_context *cms, SECItem *sigitem, SECItem *sign_content)
{
	sign_content = SECITEM_ArenaDupItem(cms->arena, sign_content);
	if (!sign_content)
		return -1;

	if (content_is_empty(sign_content->data, sign_content->len)) {
		cms->log(cms, LOG_ERR, "not signing empty digest");
		return -1;
	}

	SECOidData *oid = SECOID_FindOIDByTag(digest_get_signature_oid(cms));
	if (!oid)
		goto err;

	PK11_SetPasswordFunc(cms->func ? cms->func : SECU_GetModulePassword);
	SECKEYPrivateKey *privkey = PK11_FindKeyByAnyCert(cms->cert, cms);
	if (!privkey) {
		cms->log(cms, LOG_ERR, "could not get private key: %s",
			PORT_ErrorToString(PORT_GetError()));
		goto err;
	}

	SECItem *signature, tmp;
	memset (&tmp, '\0', sizeof (tmp));

	SECStatus status;
	status = SEC_SignData(&tmp, sign_content->data, sign_content->len,
			privkey, oid->offset);
	SECKEY_DestroyPrivateKey(privkey);
	privkey = NULL;

	if (status != SECSuccess) {
		cms->log(cms, LOG_ERR, "error signing data: %s",
			PORT_ErrorToString(PORT_GetError()));
		PORT_Free(tmp.data);
		return -1;
	}

	/* SEC_SignData awesomely allocates a SECItem and its contents for
	 * the signature, meaning they're not in our nss arena.  Fix it. */
	signature = SECITEM_AllocItem(cms->arena, NULL, tmp.len);
	if (!signature) {
		cms->log(cms, LOG_ERR, "error signing data: %s",
			PORT_ErrorToString(PORT_GetError()));
		return -1;
	}
	memcpy(signature->data, tmp.data, tmp.len);
	PORT_Free(tmp.data);

	if (status != SECSuccess) {
		cms->log(cms, LOG_ERR, "error signing data: %s",
			PORT_ErrorToString(PORT_GetError()));
		return -1;
	}
	memcpy(sigitem, signature, sizeof(*sigitem));

	//SECITEM_FreeItem(sign_content, PR_TRUE);
	return 0;
err:
	//SECITEM_FreeItem(sign_content, PR_TRUE);
	return -1;
}

Project: pesign
File: signer_info.c

generate_signed_attributes(cms_context *cms, SECItem *sattrs)
{
	Attribute *attrs[5];
	memset(attrs, '\0', sizeof (attrs));

	SECItem encoded;
	SECOidTag tag;
	SECOidData *oid;

	/* build the first attribute, which says we have no S/MIME
	 * capabilities whatsoever */
	attrs[0] = PORT_ArenaZAlloc(cms->arena, sizeof (Attribute));
	if (!attrs[0])
		goto err;

	oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_SMIME_CAPABILITIES);
	attrs[0]->attrType = oid->oid;

	SECItem *smime_caps[2] = { NULL, NULL};
	if (generate_empty_sequence(cms, &encoded) < 0)
		goto err;
	smime_caps[0] = SECITEM_ArenaDupItem(cms->arena, &encoded);
	attrs[0]->attrValues = smime_caps;

	/* build the second attribute, which says that this is
	 * a PKCS9 content blob thingy */
	attrs[1] = PORT_ArenaZAlloc(cms->arena, sizeof (Attribute));
	if (!attrs[1])
		goto err;

	oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_CONTENT_TYPE);
	attrs[1]->attrType = oid->oid;

	SECItem *content_types[2] = { NULL, NULL };
	tag = find_ms_oid_tag(SPC_INDIRECT_DATA_OBJID);
	if (tag == SEC_OID_UNKNOWN)
		goto err;
	if (generate_object_id(cms, &encoded, tag) < 0)
		goto err;
	content_types[0] = SECITEM_ArenaDupItem(cms->arena, &encoded);
	if (!content_types[0])
		goto err;
	attrs[1]->attrValues = content_types;

	/* build the third attribute.  This is our signing time. */
	attrs[2] = PORT_ArenaZAlloc(cms->arena, sizeof (Attribute));
	if (!attrs[2])
		goto err;

	oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_SIGNING_TIME);
	attrs[2]->attrType = oid->oid;

	SECItem *signing_time[2] = { NULL, NULL };
	if (generate_time(cms, &encoded, time(NULL)) < 0)
		goto err;
	signing_time[0] = SECITEM_ArenaDupItem(cms->arena, &encoded);
	if (!signing_time[0])
		goto err;
	attrs[2]->attrValues = signing_time;

	/* build the fourth attribute, which is our PKCS9 message
	 * digest (which is a SHA-whatever selected and generated elsewhere */
	attrs[3] = PORT_ArenaZAlloc(cms->arena, sizeof (Attribute));
	if (!attrs[3])
		goto err;

	oid = SECOID_FindOIDByTag(SEC_OID_PKCS9_MESSAGE_DIGEST);
	attrs[3]->attrType = oid->oid;

	SECItem *digest_values[2] = { NULL, NULL };
	if (generate_octet_string(cms, &encoded, cms->ci_digest) < 0)
		goto err;
	digest_values[0] = SECITEM_ArenaDupItem(cms->arena, &encoded);
	if (!digest_values[0])
		goto err;
	attrs[3]->attrValues = digest_values;

	Attribute **attrtmp = attrs;
	if (SEC_ASN1EncodeItem(cms->arena, sattrs, &attrtmp,
				AttributeSetTemplate) == NULL)
		goto err;
	return 0;
err:
	return -1;
}

Project: pesign
File: cms_pe_common.c

get_str(Pe *pe, char *strnum)
{
	size_t sz;
	unsigned long num;
	char *strtab;
	uint32_t strtabsz;

	/* no idea what the real max size for these is, so... we're not going
	 * to have 4B strings, and this can't be the end of the binary, so
	 * this is big enough. */
	sz = strnlen(strnum, 11);
	if (sz == 11)
		return NULL;

	errno = 0;
	num = strtoul(strnum, NULL, 10);
	if (errno != 0)
		return NULL;

	strtab = get_strtab(pe);
	if (!strtab)
		return NULL;

	strtabsz = *(uint32_t *)strtab;
	if (num >= strtabsz)
		return NULL;

	if (strnlen(&strtab[num], strtabsz - num) > strtabsz - num - 1)
		return NULL;

	return &strtab[num];
}

Project: pesign
File: cms_common.c

find_certificate(cms_context *cms, int needs_private_key)
{
	struct validity_cbdata cbd;
	if (!cms->certname || !*cms->certname) {
		cms->log(cms, LOG_ERR, "no certificate name specified");
		return -1;
	}

	PK11_SetPasswordFunc(cms->func ? cms->func : SECU_GetModulePassword);

	PK11SlotList *slots = NULL;
	slots = PK11_GetAllTokens(CKM_RSA_PKCS, PR_FALSE, PR_TRUE, cms);
	if (!slots)
		cmsreterr(-1, cms, "could not get pk11 token list");

	PK11SlotListElement *psle = NULL;
	psle = PK11_GetFirstSafe(slots);
	if (!psle) {
		save_port_err() {
			PK11_FreeSlotList(slots);
		}
		cmsreterr(-1, cms, "could not get pk11 safe");
	}

	while (psle) {
		if (!strcmp(cms->tokenname, PK11_GetTokenName(psle->slot)))
			break;

		psle = PK11_GetNextSafe(slots, psle, PR_FALSE);
	}

	if (!psle) {
		save_port_err() {
			PK11_FreeSlotList(slots);
		}
		nssreterr(-1, "Could not find token \"%s\"", cms->tokenname);
	}

	SECStatus status;
	if (PK11_NeedLogin(psle->slot) && !PK11_IsLoggedIn(psle->slot, cms)) {
		status = PK11_Authenticate(psle->slot, PR_TRUE, cms);
		if (status != SECSuccess) {
			save_port_err() {
				PK11_DestroySlotListElement(slots, &psle);
				PK11_FreeSlotList(slots);
				cms->log(cms, LOG_ERR,
					 "authentication failed for token \"%s\"",
					 cms->tokenname);
			}
			return -1;
		}
	}

	CERTCertList *certlist = NULL;
	certlist = PK11_ListCertsInSlot(psle->slot);
	if (!certlist) {
		save_port_err() {
			PK11_DestroySlotListElement(slots, &psle);
			PK11_FreeSlotList(slots);
		}
		cmsreterr(-1, cms, "could not get certificate list");
	}

	SECItem nickname = {
		.data = (void *)cms->certname,
		.len = strlen(cms->certname) + 1,
		.type = siUTF8String,
	};

	cms->psle = psle;

	cbd.cms = cms;
	cbd.psle = psle;
	cbd.slot = psle->slot;
	cbd.cert = NULL;

	if (needs_private_key) {
		status = PK11_TraverseCertsForNicknameInSlot(&nickname,
					psle->slot, is_valid_cert, &cbd);
	} else {
		status = PK11_TraverseCertsForNicknameInSlot(&nickname,
					psle->slot,
					is_valid_cert_without_private_key,
					&cbd);
	}
	if (status == SECSuccess && cbd.cert != NULL) {
		if (cms->cert)
			CERT_DestroyCertificate(cms->cert);
		cms->cert = CERT_DupCertificate(cbd.cert);
	}

	save_port_err() {
		CERT_DestroyCertList(certlist);
		PK11_DestroySlotListElement(slots, &psle);
		PK11_FreeSlotList(slots);
		cms->psle = NULL;
	}
	if (status != SECSuccess || cms->cert == NULL)
		cmsreterr(-1, cms, "could not find certificate in list");

	return 0;
}

Project: pesign
File: cms_common.c

encode_algorithm_id(cms_context *cms, SECItem *der, SECOidTag tag)
{
	SECAlgorithmID id;

	int rc = generate_algorithm_id(cms, &id, tag);
	if (rc < 0)
		return rc;

	void *ret;
	ret = SEC_ASN1EncodeItem(cms->arena, der, &id,
						SECOID_AlgorithmIDTemplate);
	if (ret == NULL)
		cmsreterr(-1, cms, "could not encode Algorithm ID");

	return 0;
}

Project: pesign
File: daemon.c

handle_event(context *ctx, struct pollfd *pollfd)
{
	struct msghdr msg;
	struct iovec iov;
	ssize_t n;
	pesignd_msghdr pm;

	msg.msg_name = NULL;
	msg.msg_namelen = 0;

	iov.iov_base = &pm;
	iov.iov_len = sizeof(pm);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	char control[1024];
	msg.msg_controllen = 1024;
	msg.msg_control = control;

	n = recvmsg(pollfd->fd, &msg, MSG_WAITALL);
	if (n < 0) {
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_WARNING,
			"recvmsg failed: %m");
		return n;
	}

	/* if recvmsg returned 0, we're not going to get any valid data. */
	/* This *probably* means we were hung up on. */
	if (n == 0)
		return n;

	if (n < (long long)sizeof (pm)) {
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_ERR,
			"got message with invalid size %zu", n);
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_ERR,
			"possible exploit attempt.  closing.");
		close(pollfd->fd);
		return -1;
	}

	if (pm.version != PESIGND_VERSION) {
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_ERR,
			"got version %#x, expected version %#x",
			pm.version, PESIGND_VERSION);
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_ERR,
			"possible exploit attempt.  closing.");
		close(pollfd->fd);
		return -1;
	}

	for (int i = 0; cmd_table[i].cmd != CMD_LIST_END; i++) {
		if (cmd_table[i].cmd == pm.command) {
			if (cmd_table[i].func == NULL) {
				handle_invalid_input(pm.command, ctx, pollfd,
							pm.size);
				close(pollfd->fd);
			}
			cmd_table[i].func(ctx, pollfd, pm.size);
			return 0;
		}
	}

	handle_invalid_input(pm.command, ctx, pollfd, pm.size);
	close(pollfd->fd);
	return 0;
}

Project: pesign
File: password.c

SECU_FilePasswd(PK11SlotInfo *slot, PRBool retry, void *arg)
{
	cms_context *cms = (cms_context *)arg;
	int fd;
	char *file = NULL;
	char *token_name = slot ? PK11_GetTokenName(slot) : NULL;
	struct token_pass *phrases = NULL;
	size_t nphrases = 0;
	char *phrase = NULL;
	char *start;
	char *ret = NULL;
	char *path;

	ingress();
	dprintf("token_name: %s", token_name);
	path = cms->pwdata.data;

	if (!path || retry)
		goto err;

	phrases = calloc(1, sizeof(struct token_pass));
	if (!phrases)
		goto err;

	fd = open(path, O_RDONLY|O_CLOEXEC);
	if (fd < 0) {
		goto err_phrases;
	} else {
		size_t file_len = 0;
		int rc;
		rc = read_file(fd, &file, &file_len);
		set_errno_guard();
		close(fd);

		if (rc < 0 || file_len < 1)
			goto err_file;
		file[file_len-1] = '\0';
		dprintf("file_len:%zd", file_len);
		dprintf("file:\"%s\"", file);

		unbreak_line_continuations(file, file_len);
	}

	start = file;
	while (start && start[0]) {
		size_t span;
		struct token_pass *new_phrases;
		int rc;
		char c;

		new_phrases = reallocarray(phrases, nphrases + 1, sizeof(struct token_pass));
		if (!new_phrases)
			goto err_phrases;
		phrases = new_phrases;
		memset(&new_phrases[nphrases], 0, sizeof(struct token_pass));

		span = strspn(start, whitespace_and_eol_chars);
		dprintf("whitespace span is %zd", span);
		start += span;
		span = strcspn(start, eol_chars);
		dprintf("non-whitespace span is %zd", span);

		c = start[span];
		start[span] = '\0';
		dprintf("file:\"%s\"", file);
		rc = parse_pwfile_line(start, &phrases[nphrases++]);
		dprintf("parse_pwfile_line returned %d", rc);
		if (rc < 0)
			goto err_phrases;

		if (c != '\0')
			span++;
		start += span;
		dprintf("start is file[%ld] == '\\x%02hhx'", start - file, start[0]);
	}

	qsort(phrases, nphrases, sizeof(struct token_pass), token_pass_cmp);
	cms->pwdata.source = PW_DATABASE;
	xfree(cms->pwdata.data);
	cms->pwdata.pwdb.phrases = phrases;
	cms->pwdata.pwdb.nphrases = nphrases;

	for (size_t i = 0; i < nphrases; i++) {
		if (phrases[i].token == NULL || phrases[i].token[0] == '\0'
		    || (token_name && !strcmp(token_name, phrases[i].token))) {
			phrase = phrases[i].pass;
			break;
		}
	}

	if (phrase) {
		ret = PORT_Strdup(phrase);
		if (!ret)
			errno = ENOMEM;
	}

err_file:
	xfree(file);
err_phrases:
	xfree(phrases);
err:
	dprintf("ret:\"%s\"", ret ? ret : "(null)");
	egress();
	return ret;
}

Project: pesign
File: daemon.c

add_token_to_authenticated_list(context *ctx, uint8_t *tokenname)
{
	char *tmp;
	uint8_t **newtokennames = realloc(ctx->tokennames,
					sizeof (uint8_t *)
					* (ctx->ntokennames+1));
	if (!newtokennames)
		return -1;
	ctx->tokennames = newtokennames;

	tmp = strdup((char *)tokenname);
	if (!tmp)
		return -1;

	newtokennames[ctx->ntokennames++] = (uint8_t *)tmp;

	qsort(newtokennames, ctx->ntokennames, sizeof (char *), cmpstringp);
	return 0;
}

Project: pesign
File: cms_common.c

generate_signature(cms_context *cms)
{
	int rc = 0;

	if (cms->digests[cms->selected_digest].pe_digest == NULL) {
		cms->log(cms, LOG_ERR, "%s:%s:%d PE digest has not been "
			"allocated", __FILE__, __func__, __LINE__);
		return -1;
	}

	if (content_is_empty(cms->digests[cms->selected_digest].pe_digest->data,
			cms->digests[cms->selected_digest].pe_digest->len)) {
		cms->log(cms, LOG_ERR, "%s:%s:%d PE binary has not been "
			"digested", __FILE__, __func__, __LINE__);
		return -1;
	}

	SECItem sd_der;
	memset(&sd_der, '\0', sizeof(sd_der));
	rc = generate_spc_signed_data(cms, &sd_der);
	if (rc < 0)
		cmsreterr(-1, cms, "could not create signed data");

	memcpy(&cms->newsig, &sd_der, sizeof (cms->newsig));
	cms->newsig.data = malloc(sd_der.len);
	if (!cms->newsig.data)
		cmsreterr(-1, cms, "could not allocate signed data");
	memcpy(cms->newsig.data, sd_der.data, sd_der.len);
	return 0;
}

Project: pesign
File: cms_pe_common.c

get_strtab(Pe *pe)
{
	static void *ret = NULL;
	uint32_t *ptr;
	intptr_t intret = 0;
	struct pe_hdr pehdr;
	void *map = NULL;
	size_t map_size = 0;

	if (ret)
		return ret;

	if (pe_getpehdr(pe, &pehdr) == NULL)
		pereterr(NULL, "invalid PE file header");

	map = pe_rawfile(pe, &map_size);
	if (!map || map_size < 1)
		return 0;

	if (pehdr.symbol_table == 0)
		return NULL;

	intret = (intptr_t)pehdr.symbol_table;
	intret += pehdr.symbols * sizeof(struct pe_symtab_entry);

	ptr = (uint32_t *)((intptr_t)map + intret);
	if (!check_pointer_and_size(pe, ptr, 4))
		pereterr(NULL, "invalid string table start");

	if (!check_pointer_and_size(pe, ptr, *ptr))
		pereterr(NULL, "invalid string table size");
	ret = ptr;
	return ret;
}

Project: pesign
File: daemon.c

write_pid_file(int pid)
{
	int fd = open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0644);
	if (fd < 0) {
err:
		fprintf(stderr, "couldn't open pidfile: %m\n");
		exit(1);
	}
	char *pidstr = NULL;
	int rc = asprintf(&pidstr, "%d\n", pid);
	if (rc < 0)
		goto err;

	rc = write(fd, pidstr, strlen(pidstr)+1);
	if (rc < 0)
		goto err;

	free(pidstr);
	close(fd);
}

Project: pesign
File: cms_common.c

find_slot_for_token(cms_context *cms, PK11SlotInfo **slot)
{
	if (!cms->tokenname) {
		cms->log(cms, LOG_ERR, "no token name specified");
		return -1;
	}

	PK11_SetPasswordFunc(cms->func ? cms->func : SECU_GetModulePassword);

	PK11SlotList *slots = NULL;
	slots = PK11_GetAllTokens(CKM_RSA_PKCS, PR_FALSE, PR_TRUE, cms);
	if (!slots)
		cmsreterr(-1, cms, "could not get pk11 token list");

	PK11SlotListElement *psle = NULL;
	psle = PK11_GetFirstSafe(slots);
	if (!psle) {
		save_port_err() {
			PK11_FreeSlotList(slots);
		}
		cmsreterr(-1, cms, "could not get pk11 safe");
	}

	while (psle) {
		if (!strcmp(cms->tokenname, PK11_GetTokenName(psle->slot)))
			break;

		psle = PK11_GetNextSafe(slots, psle, PR_FALSE);
	}

	if (!psle) {
		save_port_err() {
			PK11_FreeSlotList(slots);
		}
		nssreterr(-1, "Could not find token \"%s\"", cms->tokenname);
	}

	SECStatus status;
	if (PK11_NeedLogin(psle->slot) && !PK11_IsLoggedIn(psle->slot, cms)) {
		status = PK11_Authenticate(psle->slot, PR_TRUE, cms);
		if (status != SECSuccess) {
			save_port_err() {
				PK11_DestroySlotListElement(slots, &psle);
				PK11_FreeSlotList(slots);
				cms->log(cms, LOG_ERR,
					 "authentication failed for token \"%s\"",
					 cms->tokenname);
			}
			return -1;
		}
	}
	*slot = psle->slot;
	return 0;
}

Project: pesign
File: cms_common.c

unlock_nss_token(cms_context *cms)
{
	PK11_SetPasswordFunc(cms->func ? cms->func : SECU_GetModulePassword);

	PK11SlotList *slots = NULL;
	slots = PK11_GetAllTokens(CKM_RSA_PKCS, PR_FALSE, PR_TRUE, cms);
	if (!slots)
		cmsreterr(-1, cms, "could not get pk11 token list");

	PK11SlotListElement *psle = NULL;
	psle = PK11_GetFirstSafe(slots);
	if (!psle) {
		save_port_err() {
			PK11_FreeSlotList(slots);
		}
		cmsreterr(-1, cms, "could not get pk11 safe");
	}

	while (psle) {
		if (!strcmp(cms->tokenname, PK11_GetTokenName(psle->slot)))
			break;

		psle = PK11_GetNextSafe(slots, psle, PR_FALSE);
	}

	if (!psle) {
		save_port_err() {
			PK11_FreeSlotList(slots);
		}
		nssreterr(-1, "Could not find token \"%s\"", cms->tokenname);
	}

	SECStatus status;
	if (PK11_NeedLogin(psle->slot) &&
			!PK11_IsLoggedIn(psle->slot, cms)) {
		status = PK11_Authenticate(psle->slot, PR_TRUE, cms);
		if (status != SECSuccess) {
			save_port_err() {
				PK11_DestroySlotListElement(slots, &psle);
				PK11_FreeSlotList(slots);
				cms->log(cms, LOG_ERR,
					 "authentication failed for token \"%s\"",
					 cms->tokenname);
			}
			return -1;
		}
	}

	PK11_DestroySlotListElement(slots, &psle);
	PK11_FreeSlotList(slots);
	return 0;
}

Project: pesign
File: cms_common.c

is_valid_cert(CERTCertificate *cert, void *data)
{
	struct validity_cbdata *cbd = (struct validity_cbdata *)data;
	PK11SlotInfo *slot = cbd->slot;
	SECKEYPrivateKey *privkey = NULL;

	if (cert == NULL)
		return SECFailure;

	privkey = PK11_FindPrivateKeyFromCert(slot, cert, cbd->cms);
	if (privkey != NULL) {
		cbd->cert = CERT_DupCertificate(cert);
		SECKEY_DestroyPrivateKey(privkey);
		return SECSuccess;
	}
	return SECFailure;
}

Project: pesign
File: flatpak-dir.c

cmpstringp(const void *p1, const void *p2)
{
	return strcmp(*(char * const *)p1, *(char * const *)p2);
}

Project: pesign
File: cms_common.c

wrap_in_set(cms_context *cms, SECItem *der, SECItem **items)
{
	void *ret;

	ret = SEC_ASN1EncodeItem(cms->arena, der, &items, &SetTemplate);
	if (ret == NULL)
		cmsreterr(-1, cms, "could not encode set");
	return 0;
}

Project: pesign
File: password.c

SECU_GetPasswordString(void *arg UNUSED, char *prompt)
{
	char *ret;
	ingress();
	ret = get_password(stdin, stdout, prompt, check_password);
	egress();
	return ret;
}

Project: pesign
File: cms_common.c

cms_context_fini(cms_context *cms)
{
	if (cms->cert) {
		CERT_DestroyCertificate(cms->cert);
		cms->cert = NULL;
	}

	switch (cms->pwdata.source) {
	case PW_SOURCE_INVALID:
	case PW_PROMPT:
	case PW_DEVICE:
	case PW_FROMFILEDB:
	case PW_FROMENV:
	case PW_SOURCE_MAX:
		break;
	case PW_DATABASE:
		xfree(cms->pwdata.data);
		break;
	case PW_PLAINTEXT:
		memset(cms->pwdata.data, 0, strlen(cms->pwdata.data));
		xfree(cms->pwdata.data);
		break;
	}
	cms->pwdata.source = PW_SOURCE_INVALID;
	cms->pwdata.orig_source = PW_SOURCE_INVALID;

	if (cms->privkey) {
		free(cms->privkey);
		cms->privkey = NULL;
	}


	/* These were freed when the arena was destroyed */
	if (cms->tokenname)
		cms->tokenname = NULL;
	if (cms->certname)
		cms->certname = NULL;

	if (cms->newsig.data) {
		free_poison(cms->newsig.data, cms->newsig.len);
		free(cms->newsig.data);
		memset(&cms->newsig, '\0', sizeof (cms->newsig));
	}

	cms->selected_digest = -1;

	if (cms->ci_digest) {
		free_poison(cms->ci_digest->data, cms->ci_digest->len);
		/* XXX sure seems like we should be freeing it here, but
		 * that's segfaulting, and we know it'll get cleaned up with
		 * PORT_FreeArena a couple of lines down.
		 */
		cms->ci_digest = NULL;
	}

	teardown_digests(cms);

	if (cms->raw_signed_attrs) {
		free_poison(cms->raw_signed_attrs->data,
				cms->raw_signed_attrs->len);
		/* XXX sure seems like we should be freeing it here, but
		 * that's segfaulting, and we know it'll get cleaned up with
		 * PORT_FreeArena a couple of lines down.
		 */
		cms->raw_signed_attrs = NULL;
	}

	if (cms->raw_signature) {
		free_poison(cms->raw_signature->data,
				cms->raw_signature->len);
		/* XXX sure seems like we should be freeing it here, but
		 * that's segfaulting, and we know it'll get cleaned up with
		 * PORT_FreeArena a couple of lines down.
		 */
		cms->raw_signature = NULL;
	}

	for (int i = 0; i < cms->num_signatures; i++) {
		free(cms->signatures[i]->data);
		free(cms->signatures[i]);
	}

	xfree(cms->signatures);
	cms->num_signatures = 0;

	if (cms->authbuf) {
		xfree(cms->authbuf);
		cms->authbuf_len = 0;
	}

	PORT_FreeArena(cms->arena, PR_TRUE);
	memset(cms, '\0', sizeof(*cms));
	xfree(cms);
}

Project: pesign
File: daemon.c

handle_sign_detached_with_file_type(context *ctx, struct pollfd *pollfd, socklen_t size)
{
	handle_sign_helper(ctx, pollfd, size, 0, true);
}

Project: pesign
File: cms_common.c

generate_octet_string(cms_context *cms, SECItem *encoded, SECItem *original)
{
	if (content_is_empty(original->data, original->len)) {
		cms->log(cms, LOG_ERR, "content is empty, not encoding");
		return -1;
	}
	if (SEC_ASN1EncodeItem(cms->arena, encoded, original,
			SEC_OctetStringTemplate) == NULL)
		cmsreterr(-1, cms, "could not encode octet string");

	return 0;
}

Project: pesign
File: daemon.c

handle_invalid_input(pesignd_cmd cmd, context *ctx,
		     struct pollfd *pollfd UNUSED,
		     socklen_t size UNUSED)
{
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_ERR,
			"got unexpected command 0x%x", cmd);
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_ERR,
			"possible exploit attempt");
}

Project: pesign
File: daemon.c

steal_from_cms(cms_context *old, cms_context *new)
{
	new->tokenname = old->tokenname;
	new->certname = old->certname;

	new->selected_digest = old->selected_digest;

	new->log = old->log;
	new->log_priv = old->log_priv;
}

Project: pesign
File: cms_common.c

generate_object_id(cms_context *cms, SECItem *der, SECOidTag tag)
{
	SECOidData *oid;

	oid = SECOID_FindOIDByTag(tag);
	if (!oid)
		cmsreterr(-1, cms, "could not find OID");

	void *ret;
	ret = SEC_ASN1EncodeItem(cms->arena, der, &oid->oid,
						SEC_ObjectIDTemplate);
	if (ret == NULL)
		cmsreterr(-1, cms, "could not encode ODI");
	return 0;
}

Project: pesign
File: cms_pe_common.c

generate_digest(cms_context *cms, Pe *pe, int padded)
{
	void *hash_base;
	size_t hash_size;
	struct pe32_opt_hdr *pe32opthdr = NULL;
	struct pe32plus_opt_hdr *pe64opthdr = NULL;
	unsigned long hashed_bytes = 0;
	void *opthdr;
	int rc = -1;

	if (!pe) {
		cms->log(cms, LOG_ERR, "no output pe ready");
		return -1;
	}

	rc = generate_digest_begin(cms);
	if (rc < 0)
		return rc;

	struct pe_hdr pehdr;
	if (pe_getpehdr(pe, &pehdr) == NULL)
		pereterr(-1, "invalid PE file header");

	void *map = NULL;
	size_t map_size = 0;

	/* 1. Load the image header into memory - should be done
	 * 2. Initialize SHA hash context. */
	map = pe_rawfile(pe, &map_size);
	if (!map)
		pereterr(-1, "could not get raw output file address");

	/* 3. Calculate the distance from the base of the image header to the
	 * image checksum.
	 * 4. Hash the image header from start to the beginning of the
	 * checksum. */
	hash_base = map;

	opthdr = pe_getopthdr(pe);
	if (opthdr == NULL) {
		cms->log(cms, LOG_ERR, "%s:%s:%d PE header is invalid", __FILE__, __func__, __LINE__);
		goto error;
	}

	switch (pe_kind(pe)) {
	case PE_K_PE_EXE: {
		pe32opthdr = opthdr;
		hash_size = (uintptr_t)&pe32opthdr->csum - (uintptr_t)hash_base;
		break;
	}
	case PE_K_PE64_EXE: {
		pe64opthdr = opthdr;
		hash_size = (uintptr_t)&pe64opthdr->csum - (uintptr_t)hash_base;
		break;
	}
	default:
		goto error;
	}
	if (!check_pointer_and_size(pe, hash_base, hash_size)) {
		cms->log(cms, LOG_ERR, "%s:%s:%d PE header is invalid",
			__FILE__, __func__, __LINE__);
		goto error;
	}
	dprintf("beginning of hash\n");
	dprintf("digesting %lx + %lx\n", hash_base - map, hash_size);
	generate_digest_step(cms, hash_base, hash_size);

	/* 5. Skip over the image checksum
	 * 6. Get the address of the beginning of the cert dir entry
	 * 7. Hash from the end of the csum to the start of the cert dirent. */
	hash_base += hash_size;
	hash_base += pe32opthdr ? sizeof(pe32opthdr->csum)
				: sizeof(pe64opthdr->csum);
	data_directory *dd;

	rc = pe_getdatadir(pe, &dd);
	if (rc < 0 || !dd || !check_pointer_and_size(pe, dd, sizeof(*dd))) {
		cms->log(cms, LOG_ERR, "%s:%s:%d PE data directory is invalid",
			__FILE__, __func__, __LINE__);
		goto error;
	}

	hash_size = (uintptr_t)&dd->certs - (uintptr_t)hash_base;
	if (!check_pointer_and_size(pe, hash_base, hash_size)) {
		cms->log(cms, LOG_ERR, "%s:%s:%d PE data directory is invalid",
			__FILE__, __func__, __LINE__);
		goto error;
	}
	generate_digest_step(cms, hash_base, hash_size);
	dprintf("digesting %lx + %lx\n", hash_base - map, hash_size);

	/* 8. Skip over the crt dir
	 * 9. Hash everything up to the end of the image header. */
	hash_base = &dd->base_relocations;
	hash_size = (pe32opthdr ? pe32opthdr->header_size
				: pe64opthdr->header_size) -
		((uintptr_t)&dd->base_relocations - (uintptr_t)map);

	if (!check_pointer_and_size(pe, hash_base, hash_size)) {
		cms->log(cms, LOG_ERR, "%s:%s:%d PE relocations table is "
			"invalid", __FILE__, __func__, __LINE__);
		goto error;
	}
	generate_digest_step(cms, hash_base, hash_size);
	dprintf("digesting %lx + %lx\n", hash_base - map, hash_size);

	/* 10. Set SUM_OF_BYTES_HASHED to the size of the header. */
	hashed_bytes = pe32opthdr ? pe32opthdr->header_size
				: pe64opthdr->header_size;

	struct section_header *shdrs = calloc(pehdr.sections, sizeof (*shdrs));
	if (!shdrs)
		goto error;
	Pe_Scn *scn = NULL;
	for (int i = 0; i < pehdr.sections; i++) {
		scn = pe_nextscn(pe, scn);
		if (scn == NULL)
			break;
		pe_getshdr(scn, &shdrs[i]);
	}
	sort_shdrs(shdrs, pehdr.sections - 1);

	for (int i = 0; i < pehdr.sections; i++) {
		if (shdrs[i].raw_data_size == 0)
			continue;

		hash_base = (void *)((uintptr_t)map + shdrs[i].data_addr);
		hash_size = shdrs[i].raw_data_size;

		if (!check_pointer_and_size(pe, hash_base, hash_size)) {
			cms->log(cms, LOG_ERR, "%s:%s:%d PE section \"%s\" "
				"has invalid address",
				__FILE__, __func__, __LINE__, shdrs[i].name);
			goto error_shdrs;
		}

		if (cms->omit_vendor_cert) {
			char *name = shdrs[i].name;
			if (name && name[0] == '/')
				name = get_str(pe, name + 1);
			dprintf("section:\"%s\"\n", name ? name : "(null)");
			if (name && !strcmp(name, ".vendor_cert")) {
				dprintf("skipping .vendor_cert section\n");
				hashed_bytes += hash_size;
				continue;
			}
		}

		generate_digest_step(cms, hash_base, hash_size);
		dprintf("digesting %lx + %lx\n", hash_base - map, hash_size);

		hashed_bytes += hash_size;
	}

	if (map_size > hashed_bytes) {
		hash_base = (void *)((uintptr_t)map + hashed_bytes);
		hash_size = map_size - dd->certs.size - hashed_bytes;

		if (!check_pointer_and_size(pe, hash_base, hash_size)) {
			cms->log(cms, LOG_ERR, "%s:%s:%d PE has invalid "
				"trailing data", __FILE__, __func__, __LINE__);
			goto error_shdrs;
		}
		if (hash_size % 8 != 0 && padded) {
			size_t tmp_size = hash_size +
					  ALIGNMENT_PADDING(hash_size, 8);
			uint8_t tmp_array[tmp_size];
			memset(tmp_array, '\0', tmp_size);
			memcpy(tmp_array, hash_base, hash_size);
			generate_digest_step(cms, tmp_array, tmp_size);
			dprintf("digesting %lx + %lx\n", (unsigned long)tmp_array, tmp_size);
		} else {
			generate_digest_step(cms, hash_base, hash_size);
			dprintf("digesting %lx + %lx\n", hash_base - map, hash_size);
		}
	}
	dprintf("end of hash\n");

	rc = generate_digest_finish(cms);
	if (rc < 0)
		goto error_shdrs;

	if (shdrs) {
		free(shdrs);
		shdrs = NULL;
	}

	return 0;

error_shdrs:
	if (shdrs)
		free(shdrs);
error:
	return -1;
}

Project: pesign
File: cms_common.c

generate_empty_sequence(cms_context *cms, SECItem *encoded)
{
	SECItem empty = {.type = SEC_ASN1_SEQUENCE,
			 .data = NULL,
			 .len = 0
	};
	void *ret;
	ret = SEC_ASN1EncodeItem(cms->arena, encoded, &empty,
							EmptySequenceTemplate);
	if (ret == NULL)
		cmsreterr(-1, cms, "could not encode empty sequence");
	return 0;
}

Project: pesign
File: daemon.c

handle_sign_attached_with_file_type(context *ctx, struct pollfd *pollfd, socklen_t size)
{
	handle_sign_helper(ctx, pollfd, size, 1, true);
}

Project: pesign
File: cms_pe_common.c

check_pointer_and_size(Pe *pe, void *ptr, size_t size)
{
	void *map = NULL;
	size_t map_size = 0;

	map = pe_rawfile(pe, &map_size);
	if (!map || map_size < 1)
		return 0;

	if ((uintptr_t)ptr < (uintptr_t)map)
		return 0;

	if ((uintptr_t)ptr + size > (uintptr_t)map + map_size)
		return 0;

	if (ptr <= map && size >= map_size)
		return 0;

	return 1;
}

Project: pesign
File: cms_common.c

generate_digest_begin(cms_context *cms)
{
	struct digest *digests = NULL;

	if (cms->digests) {
		digests = cms->digests;
	} else {
		digests = PORT_ZAlloc(n_digest_params * sizeof (*digests));
		if (digests == NULL)
			cmsreterr(-1, cms, "could not allocate digest context");
	}

	for (int i = 0; i < n_digest_params; i++) {
		digests[i].pk11ctx = PK11_CreateDigestContext(
						digest_params[i].digest_tag);
		if (!digests[i].pk11ctx) {
			cms->log(cms, LOG_ERR, "%s:%s:%d could not create "
				"digest context: %s",
				__FILE__, __func__, __LINE__,
				PORT_ErrorToString(PORT_GetError()));
			goto err;
		}

		PK11_DigestBegin(digests[i].pk11ctx);
	}

	cms->digests = digests;
	return 0;

err:
	for (int i = 0; i < n_digest_params; i++) {
		if (digests[i].pk11ctx)
			PK11_DestroyContext(digests[i].pk11ctx, PR_TRUE);
	}

	free(digests);
	return -1;
}

Project: pesign
File: cms_common.c

generate_digest_finish(cms_context *cms)
{
	void *mark = PORT_ArenaMark(cms->arena);

	for (int i = 0; i < n_digest_params; i++) {
		SECItem *digest = PORT_ArenaZAlloc(cms->arena,sizeof (SECItem));
		if (digest == NULL) {
			cms->log(cms, LOG_ERR, "%s:%s:%d could not allocate "
				"memory: %s", __FILE__, __func__, __LINE__,
				PORT_ErrorToString(PORT_GetError()));
			goto err;
		}

		digest->type = siBuffer;
		digest->len = digest_params[i].size;
		digest->data = PORT_ArenaZAlloc(cms->arena, digest_params[i].size);
		if (digest->data == NULL) {
			cms->log(cms, LOG_ERR, "%s:%s:%d could not allocate "
				"memory: %s", __FILE__, __func__, __LINE__,
				PORT_ErrorToString(PORT_GetError()));
			goto err;
		}

		PK11_DigestFinal(cms->digests[i].pk11ctx,
			digest->data, &digest->len, digest_params[i].size);
		PK11_Finalize(cms->digests[i].pk11ctx);
		PK11_DestroyContext(cms->digests[i].pk11ctx, PR_TRUE);
		cms->digests[i].pk11ctx = NULL;
		/* XXX sure seems like we should be freeing it here,
		 * but that's segfaulting, and we know it'll get
		 * cleaned up with PORT_FreeArena a couple of lines
		 * down.
		 */
		cms->digests[i].pe_digest = digest;
	}

	PORT_ArenaUnmark(cms->arena, mark);
	return 0;
err:
	for (int i = 0; i < n_digest_params; i++) {
		if (cms->digests[i].pk11ctx)
			PK11_DestroyContext(cms->digests[i].pk11ctx, PR_TRUE);
	}
	PORT_ArenaRelease(cms->arena, mark);
	return -1;
}

Project: pesign
File: cms_common.c

make_eku_oid(cms_context *cms, SECItem *encoded, SECOidTag oid_tag)
{
	void *rv;
	SECOidData *oid_data;

	oid_data = SECOID_FindOIDByTag(oid_tag);
	if (!oid_data)
		cmsreterr(-1, cms, "could not encode eku oid data");

	rv = SEC_ASN1EncodeItem(cms->arena, encoded, &oid_data->oid,
				EKUOidSequence);
	if (rv == NULL)
		cmsreterr(-1, cms, "could not encode eku oid data");

	encoded->type = siBuffer;
	return 0;
}

Project: pesign
File: daemon.c

hide_stolen_goods_from_cms(cms_context *new,
			   cms_context *old UNUSED)
{
	new->tokenname = NULL;
	new->certname = NULL;
}

Project: pesign
File: daemon.c

handle_sign_attached(context *ctx, struct pollfd *pollfd, socklen_t size)
{
	handle_sign_helper(ctx, pollfd, size, 1, false);
}

Project: pesign
File: daemon.c

handle_unlock_token(context *ctx, struct pollfd *pollfd, socklen_t size)
{
	struct msghdr msg;
	struct iovec iov;
	ssize_t n;

	int rc = cms_context_alloc(&ctx->cms);
	if (rc < 0) {
		send_response(ctx, ctx->backup_cms, pollfd, rc);
		return;
	}

	steal_from_cms(ctx->backup_cms, ctx->cms);

	char *buffer = malloc(size);
	if (!buffer) {
oom:
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"unable to allocate memory: %m");
		exit(1);
	}

	memset(&msg, '\0', sizeof(msg));

	iov.iov_base = buffer;
	iov.iov_len = size;
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;

	n = recvmsg(pollfd->fd, &msg, MSG_WAITALL);

	pesignd_string *tn = (pesignd_string *)buffer;
	if (n < (long long)sizeof(tn->size)) {
malformed:
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"unlock-token: invalid data");
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"possible exploit attempt. closing.");
		close(pollfd->fd);
		return;
	}
	n -= sizeof(tn->size);
	if ((size_t)n < tn->size)
		goto malformed;
	n -= tn->size;

	if (tn->value[tn->size - 1] != '\0')
		goto malformed;

	pesignd_string *tp = pesignd_string_next(tn);
	if ((size_t)n < sizeof(tp->size))
		goto malformed;
	n -= sizeof(tp->size);
	if ((size_t)n < tp->size)
		goto malformed;
	n -= tp->size;

	if (tn->value[tn->size - 1] != '\0')
		goto malformed;

	if (n != 0)
		goto malformed;

	ctx->cms->log(ctx->cms, ctx->priority|LOG_NOTICE,
		"unlocking token \"%s\"", tn->value);

	/* authenticating with nss frees this ... best API ever. */
	ctx->cms->tokenname = PORT_ArenaStrdup(ctx->cms->arena,
						(char *)tn->value);
	if (!ctx->cms->tokenname)
		goto oom;

	char *pin = (char *)tp->value;
	if (!pin)
		goto oom;

	secuPWData pwdata;

	memset(&pwdata, 0, sizeof(pwdata));
	pwdata.source = pwdata.orig_source = PW_PLAINTEXT;
	pwdata.data = pin;

	cms_set_pw_callback(ctx->cms, get_password_passthrough);
	cms_set_pw_data(ctx->cms, &pwdata);

	rc = unlock_nss_token(ctx->cms);

	cms_set_pw_callback(ctx->cms, get_password_fail);
	cms_set_pw_data(ctx->cms, NULL);

	if (rc == -1)
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"could not find token \"%s\"", tn->value);
	else if (rc == 0) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_NOTICE,
			"authentication succeeded for token \"%s\"",
			tn->value);
		rc = add_token_to_authenticated_list(ctx, tn->value);
		if (rc < 0)
			ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
				"couldn't add token to internal list: %m");
	}

	send_response(ctx, ctx->cms, pollfd, rc);
	free(buffer);

	hide_stolen_goods_from_cms(ctx->cms, ctx->backup_cms);
	cms_context_fini(ctx->cms);
}

Project: pesign
File: signer_info.c

generate_unsigned_attributes(cms_context *cms, SECItem *uattrs)
{
	Attribute *attrs[1];
	memset(attrs, '\0', sizeof (attrs));

	Attribute **attrtmp = attrs;
	if (SEC_ASN1EncodeItem(cms->arena, uattrs, &attrtmp,
				AttributeSetTemplate) == NULL)
		goto err;
	return 0;
err:
	return -1;
}

Project: pesign
File: daemon.c

set_up_outpe(context *ctx, int fd, Pe *inpe, Pe **outpe)
{
	size_t size;
	char *addr;

	addr = pe_rawfile(inpe, &size);

	off_t offset = lseek(fd, 0, SEEK_SET);
	if (offset < 0) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"could not read output file: %m");
		return -1;
	}

	int rc = ftruncate(fd, size);
	if (rc < 0) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"could not extend output file: %m");
		return -1;
	}
	rc = write(fd, addr, size);
	if (rc < 0) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"could not write to output file: %m");
		return -1;
	}

	*outpe = pe_begin(fd, PE_C_RDWR_MMAP, NULL);
	if (!*outpe)
		*outpe = pe_begin(fd, PE_C_RDWR, NULL);
	if (!*outpe) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"could not set up output: %s",
			pe_errmsg(pe_errno()));
		return -1;
	}

	pe_clearcert(*outpe);
	return 0;
}

Project: pesign
File: daemon.c

sign_kmod(context *ctx, int infd, int outfd, int attached)
{
	unsigned char *map;
	struct stat statbuf;
	ssize_t sig_len;
	int rc;

	rc = fstat(infd, &statbuf);
	if (rc != 0) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"could not stat input file: %m");
		return rc;
	}

	map = mmap(NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, infd, 0);
	if (map == MAP_FAILED) {
		ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
			"could not map input file: %m");
		return -1;

	}

	rc = kmod_generate_digest(ctx->cms, map, statbuf.st_size);
	if (rc < 0)
		goto out;

	if (attached) {
		rc = write_file(outfd, map, statbuf.st_size);
		if (rc) {
			ctx->cms->log(ctx->cms, ctx->priority|LOG_ERR,
				"could not write module data: %m");
			goto out;
		}
	}

	sig_len = kmod_write_signature(ctx->cms, outfd);
	if (sig_len < 0) {
		rc = sig_len;
		goto out;
	}

	rc = kmod_write_sig_info(ctx->cms, outfd, sig_len);

out:
	munmap(map, statbuf.st_size);
	return rc;
}

Project: pesign
File: daemon.c

handle_sign_detached(context *ctx, struct pollfd *pollfd, socklen_t size)
{
	handle_sign_helper(ctx, pollfd, size, 0, false);
}

Project: pesign
File: signer_info.c

generate_spc_signer_info(cms_context *cms, SpcSignerInfo *sip)
{
	if (!sip)
		return -1;

	SpcSignerInfo si;
	memset(&si, '\0', sizeof (si));

	if (SEC_ASN1EncodeInteger(cms->arena, &si.CMSVersion, 1) == NULL) {
		cms->log(cms, LOG_ERR, "could not encode CMSVersion: %s",
			PORT_ErrorToString(PORT_GetError()));
		goto err;
	}

	si.sid.signerType = signerTypeIssuerAndSerialNumber;
	si.sid.signerValue.iasn.issuer = cms->cert->derIssuer;
	si.sid.signerValue.iasn.serial = cms->cert->serialNumber;

	if (generate_algorithm_id(cms, &si.digestAlgorithm,
			digest_get_digest_oid(cms)) < 0)
		goto err;


	if (cms->raw_signature) {
		memcpy(&si.signedAttrs, cms->raw_signed_attrs,
			sizeof (si.signedAttrs));
		memcpy(&si.signature, cms->raw_signature, sizeof(si.signature));
	} else {
		if (generate_signed_attributes(cms, &si.signedAttrs) < 0)
			goto err;

		if (sign_blob(cms, &si.signature, &si.signedAttrs) < 0)
			goto err;
	}

	si.signedAttrs.data[0] = SEC_ASN1_CONTEXT_SPECIFIC | 0 |
				SEC_ASN1_CONSTRUCTED;

	if (generate_algorithm_id(cms, &si.signatureAlgorithm,
				digest_get_encryption_oid(cms)) < 0)
		goto err;

	if (generate_unsigned_attributes(cms, &si.unsignedAttrs) < 0)
		goto err;

	memcpy(sip, &si, sizeof(si));
	return 0;
err:
	return -1;
}

Project: pesign
File: daemon.c

handle_kill_daemon(context *ctx UNUSED,
		   struct pollfd *pollfd UNUSED,
		   socklen_t size UNUSED)
{
	should_exit = 1;
}

Project: pesign
File: daemon.c

get_uid_and_gid(context *ctx, char **homedir)
{
	struct passwd *passwd;

	passwd = getpwnam("pesign");

	if (!passwd)
		return -1;

	ctx->uid = passwd->pw_uid;
	ctx->gid = passwd->pw_gid;

	if (ctx->uid == 0 || ctx->gid == 0) {
		ctx->backup_cms->log(ctx->backup_cms, ctx->priority|LOG_ERR,
			"cowardly refusing to start with uid = %d and gid = %d",
			ctx->uid, ctx->gid);
		errno = EINVAL;
		return -1;
	}

	*homedir = passwd->pw_dir;

	return 0;
}

Project: pesign
File: password.c

print_prompt(FILE *in, FILE *out, char *prompt)
{
	int infd = fileno(in);
	struct termios tio;

	if (!isatty(infd))
		return;

	fprintf(out, "%s", prompt);
	fflush(out);

	tcgetattr(infd, &tio);
	tio.c_lflag &= ~ECHO;
	tcsetattr(infd, TCSAFLUSH, &tio);
}

Project: pesign
File: cms_common.c

generate_common_name(cms_context *cms, SECItem *der, char *cn_str)
{
	CommonName cn;
	SECItem cn_item;
	int rc;

	rc = generate_object_id(cms, &cn.oid, SEC_OID_AVA_COMMON_NAME);
	if (rc < 0)
		return rc;
	rc = generate_string(cms, &cn.string, cn_str);
	if (rc < 0)
		return rc;

	void *ret;
	ret = SEC_ASN1EncodeItem(cms->arena, &cn_item, &cn, CommonNameTemplate);
	if (ret == NULL)
		cmsreterr(-1, cms, "could not encode common name");

	SECItem cn_set;
	SECItem *items[2] = {&cn_item, NULL};
	rc = wrap_in_set(cms, &cn_set, items);
	if (rc < 0)
		return rc;
	rc = wrap_in_seq(cms, der, &cn_set, 1);
	if (rc < 0)
		return rc;
	return 0;
}

Project: pesign
File: password.c

parse_pwfile_line(char *start, struct token_pass *tp)
{
	size_t span, escspan;
	char *line = start;
	size_t offset = 0;

	span = strspn(line, whitespace_and_eol_chars);
	dprintf("whitespace span is %zd", span);
	if (span == 0 && line[span] == '\0')
		return -1;
	line += span;

	tp->token = NULL;
	tp->pass = line;

	offset = 0;
	do {
		span = strcspn(line + offset, whitespace_and_eol_chars);
		escspan = strescspn(line + offset);
		if (escspan < span)
			offset += escspan + 2;
	} while(escspan < span);
	span += offset;
	dprintf("non-whitespace span is %zd", span);

	if (line[span] == '\0') {
		dprintf("returning %ld", (line + span) - start);
		return (line + span) - start;
	}
	line[span] = '\0';

	line += span + 1;
	span = strspn(line, whitespace_and_eol_chars);
	dprintf("whitespace span is %zd", span);
	line += span;
	tp->token = tp->pass;
	tp->pass = line;

	offset = 0;
	do {
		span = strcspn(line + offset, whitespace_and_eol_chars);
		escspan = strescspn(line + offset);
		if (escspan < span)
			offset += escspan + 2;
	} while(escspan < span);
	span += offset;
	dprintf("non-whitespace span is %zd", span);
	if (line[span] != '\0')
		line[span++] = '\0';

	resolve_escapes(tp->token);
	dprintf("Setting token pass %p to { %p, %p }", tp, tp->token, tp->pass);
	dprintf("token:\"%s\"", tp->token);
	dprintf("pass:\"%s\"", tp->pass);
	dprintf("returning %ld", (line + span) - start);
	return (line + span) - start;
}

Project: pesign
File: password.c

readpw(PK11SlotInfo *slot UNUSED,
       PRBool retry UNUSED,
       void *arg UNUSED)
{
	struct termios sio, tio;
	char line[LINE_MAX], *p;
	char *ret;

	ingress();
	memset(line, '\0', sizeof (line));

	if (tcgetattr(fileno(stdin), &sio) < 0) {
		warnx("Could not read password from standard input.");
		return NULL;
	}
	tio = sio;
	tio.c_lflag &= ~ECHO;
	if (tcsetattr(fileno(stdin), 0, &tio) < 0) {
		warnx("Could not read password from standard input.");
		return NULL;
	}

	fprintf(stdout, "Enter passphrase for private key: ");
	fflush(stdout);
	ret = fgets(line, sizeof(line), stdin);
	set_errno_guard();
	tcsetattr(fileno(stdin), 0, &sio);
	fprintf(stdout, "\n");
	fflush(stdout);
	if (ret == NULL)
		return NULL;

	p = line + strcspn(line, "\r\n");
	if (p == NULL)
		p = line + strcspn(line, "\n");
	if (p != NULL)
		*p = '\0';

	ret = strdup(line);
	memset(line, '\0', sizeof (line));
	if (!ret) {
		warnx("Could not read passphrase.");
		return NULL;
	}
	egress();
	return ret;
}

Project: pesign
File: pesign.c

long verbosity(void)
{
	if (!verbose)
		return 0;
	return *verbose;
}