#include "cupsd.h"
#ifdef HAVE_ACL_INIT
# include <sys/acl.h>
# ifdef HAVE_MEMBERSHIP_H
# include <membership.h>
# endif
#endif
static int ctcompare(const char *a, const char *b);
void
cupsdAddCert(int pid,
const char *username,
int type)
{
int i;
cupsd_cert_t *cert;
int fd;
char filename[1024];
static const char hex[] = "0123456789ABCDEF";
cupsdLogMessage(CUPSD_LOG_DEBUG, "cupsdAddCert: Adding certificate for PID %d", pid);
if ((cert = calloc(sizeof(cupsd_cert_t), 1)) == NULL)
return;
cert->pid = pid;
cert->type = type;
strlcpy(cert->username, username, sizeof(cert->username));
for (i = 0; i < 32; i ++)
cert->certificate[i] = hex[CUPS_RAND() & 15];
snprintf(filename, sizeof(filename), "%s/certs/%d", StateDir, pid);
unlink(filename);
if ((fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0400)) < 0)
{
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to create certificate file %s - %s",
filename, strerror(errno));
free(cert);
return;
}
if (pid == 0)
{
#ifdef HAVE_ACL_INIT
acl_t acl;
acl_entry_t entry;
acl_permset_t permset;
# ifdef HAVE_MBR_UID_TO_UUID
uuid_t group;
# endif
static int acls_not_supported = 0;
#endif
fchmod(fd, 0440);
fchown(fd, RunUser, SystemGroupIDs[0]);
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdAddCert: NumSystemGroups=%d", NumSystemGroups);
#ifdef HAVE_ACL_INIT
if (NumSystemGroups > 1)
{
int j;
# ifdef HAVE_MBR_UID_TO_UUID
acl = acl_init(NumSystemGroups - 1);
for (i = 1; i < NumSystemGroups; i ++)
{
for (j = 0; j < i; j ++)
if (SystemGroupIDs[j] == SystemGroupIDs[i])
break;
if (j < i)
continue;
acl_create_entry(&acl, &entry);
acl_get_permset(entry, &permset);
acl_add_perm(permset, ACL_READ_DATA);
acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
mbr_gid_to_uuid((gid_t)SystemGroupIDs[i], group);
acl_set_qualifier(entry, &group);
acl_set_permset(entry, permset);
}
# else
acl = acl_init(NumSystemGroups + 3);
acl_create_entry(&acl, &entry);
acl_get_permset(entry, &permset);
acl_add_perm(permset, ACL_READ);
acl_set_tag_type(entry, ACL_USER_OBJ);
acl_set_permset(entry, permset);
acl_create_entry(&acl, &entry);
acl_get_permset(entry, &permset);
acl_add_perm(permset, ACL_READ);
acl_set_tag_type(entry, ACL_GROUP_OBJ);
acl_set_permset(entry, permset);
acl_create_entry(&acl, &entry);
acl_get_permset(entry, &permset);
acl_add_perm(permset, 0);
acl_set_tag_type(entry, ACL_OTHER);
acl_set_permset(entry, permset);
acl_create_entry(&acl, &entry);
acl_get_permset(entry, &permset);
acl_add_perm(permset, ACL_READ);
acl_set_tag_type(entry, ACL_MASK);
acl_set_permset(entry, permset);
for (i = 1; i < NumSystemGroups; i ++)
{
for (j = 0; j < i; j ++)
if (SystemGroupIDs[j] == SystemGroupIDs[i])
break;
if (j < i)
continue;
acl_create_entry(&acl, &entry);
acl_get_permset(entry, &permset);
acl_add_perm(permset, ACL_READ);
acl_set_tag_type(entry, ACL_GROUP);
acl_set_qualifier(entry, SystemGroupIDs + i);
acl_set_permset(entry, permset);
}
if (acl_valid(acl))
{
char *text, *textptr;
cupsdLogMessage(CUPSD_LOG_ERROR, "ACL did not validate: %s",
strerror(errno));
text = acl_to_text(acl, NULL);
for (textptr = strchr(text, '\n');
textptr;
textptr = strchr(textptr + 1, '\n'))
*textptr = ',';
cupsdLogMessage(CUPSD_LOG_ERROR, "ACL: %s", text);
acl_free(text);
}
# endif
if (acl_set_fd(fd, acl))
{
if (errno != EOPNOTSUPP || !acls_not_supported)
cupsdLogMessage(CUPSD_LOG_ERROR,
"Unable to set ACLs on root certificate \"%s\" - %s",
filename, strerror(errno));
if (errno == EOPNOTSUPP)
acls_not_supported = 1;
}
acl_free(acl);
}
#endif
RootCertTime = time(NULL);
}
else
{
fchmod(fd, 0400);
fchown(fd, User, Group);
}
DEBUG_printf(("ADD pid=%d, username=%s, cert=%s\n", pid, username,
cert->certificate));
write(fd, cert->certificate, strlen(cert->certificate));
close(fd);
cert->next = Certs;
Certs = cert;
}
void
cupsdDeleteCert(int pid)
{
cupsd_cert_t *cert,
*prev;
char filename[1024];
for (prev = NULL, cert = Certs; cert != NULL; prev = cert, cert = cert->next)
if (cert->pid == pid)
{
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdDeleteCert: Removing certificate for PID %d.", pid);
DEBUG_printf(("DELETE pid=%d, username=%s, cert=%s\n", cert->pid,
cert->username, cert->certificate));
if (prev == NULL)
Certs = cert->next;
else
prev->next = cert->next;
free(cert);
snprintf(filename, sizeof(filename), "%s/certs/%d", StateDir, pid);
if (unlink(filename))
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to remove %s!", filename);
return;
}
}
void
cupsdDeleteAllCerts(void)
{
cupsd_cert_t *cert,
*next;
char filename[1024];
for (cert = Certs; cert != NULL; cert = next)
{
snprintf(filename, sizeof(filename), "%s/certs/%d", StateDir, cert->pid);
if (unlink(filename))
cupsdLogMessage(CUPSD_LOG_ERROR, "Unable to remove %s!", filename);
next = cert->next;
free(cert);
}
Certs = NULL;
RootCertTime = 0;
}
cupsd_cert_t *
cupsdFindCert(const char *certificate)
{
cupsd_cert_t *cert;
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindCert(certificate=%s)", certificate);
for (cert = Certs; cert != NULL; cert = cert->next)
if (!ctcompare(certificate, cert->certificate))
{
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindCert: Returning \"%s\".", cert->username);
return (cert);
}
cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdFindCert: Certificate not found.");
return (NULL);
}
void
cupsdInitCerts(void)
{
#ifndef HAVE_ARC4RANDOM
cups_file_t *fp;
if ((fp = cupsFileOpen("/dev/urandom", "rb")) == NULL)
{
struct timeval tod;
gettimeofday(&tod, NULL);
CUPS_SRAND((unsigned)(tod.tv_sec + tod.tv_usec));
}
else
{
unsigned seed;
seed = (unsigned)cupsFileGetChar(fp);
seed = (seed << 8) | (unsigned)cupsFileGetChar(fp);
seed = (seed << 8) | (unsigned)cupsFileGetChar(fp);
CUPS_SRAND((seed << 8) | (unsigned)cupsFileGetChar(fp));
cupsFileClose(fp);
}
#endif
if (!RunUser)
cupsdAddCert(0, "root", cupsdDefaultAuthType());
}
static int
ctcompare(const char *a,
const char *b)
{
int result = 0;
while (*a && *b)
{
result |= *a ^ *b;
a ++;
b ++;
}
return (result);
}