diff --git a/.gitignore b/.gitignore
index 61887bec7..9f46b6001 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
/configure~
/cups3.pc
/Makedefs
+/cups/.testssl
/cups/fuzzipp
/cups/libcups.a
/cups/libcups.dylib
@@ -23,6 +24,7 @@
/cups/locale/
/cups/rasterbench
/cups/test.json
+/cups/test.log
/cups/test.pwg
/cups/test.raster
/cups/testarray
diff --git a/Makedefs.in b/Makedefs.in
index a8edba1ef..8824d7981 100644
--- a/Makedefs.in
+++ b/Makedefs.in
@@ -49,14 +49,6 @@ INSTALL_LIB = $(INSTALL) -c -m 755
INSTALL_MAN = $(INSTALL) -c -m 444
-#
-# Cross-compilation support: "local" target is used for any tools that are
-# built and run locally.
-#
-
-LOCALTARGET = @LOCALTARGET@
-
-
#
# Libraries...
#
diff --git a/Makefile b/Makefile
index 2e8353f97..76acc66bc 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
#
# Top-level Makefile for libcups.
#
-# Copyright © 2020-2022 by OpenPrinting
+# Copyright © 2020-2023 by OpenPrinting
#
# Licensed under Apache License v2.0. See the file "LICENSE" for more
# information.
@@ -100,6 +100,17 @@ uninstall:
done
+#
+# Build all unit tests...
+#
+
+unittests: all
+ for dir in $(DIRS); do\
+ echo "======== unittests in $$dir ========";\
+ (cd $$dir ; $(MAKE) $(MFLAGS) unittests) || exit 1;\
+ done
+
+
#
# Test everything...
#
diff --git a/configure b/configure
index fb9c87b08..c16cb2bc1 100755
--- a/configure
+++ b/configure
@@ -684,7 +684,6 @@ CC
OPTIM
DSOFLAGS
CODE_SIGN
-LOCALTARGET
host_os
host_vendor
host_cpu
@@ -745,7 +744,7 @@ enable_static
enable_shared
enable_debug
enable_maintainer
-enable_sanitizer
+with_sanitizer
with_dsoflags
with_ldflags
with_domainsocket
@@ -1386,13 +1385,14 @@ Optional Features:
--disable-shared do not install shared library
--enable-debug turn on debugging, default=no
--enable-maintainer turn on maintainer mode, default=no
- --enable-sanitizer build with AddressSanitizer, default=no
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
--with-dnssd=LIBRARY set DNS-SD library (auto, avahi, mdnsresponder)
--with-tls=... use gnutls or openssl/libressl for TLS support
+ --with-sanitizer build with address, leak, memory, thread, or
+ undefined sanitizer, default=no
--with-dsoflags=... Specify additional DSOFLAGS
--with-ldflags=... Specify additional LDFLAGS
--with-domainsocket set unix domain socket name
@@ -2558,20 +2558,6 @@ then :
fi
-if test "$build" = "$host"
-then :
-
- # No, build local targets
- LOCALTARGET="local"
-
-else $as_nop
-
- # Yes, don't build local targets
- LOCALTARGET=""
-
-fi
-
-
for ac_prog in codesign true
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
@@ -5217,12 +5203,24 @@ then :
enableval=$enable_maintainer;
fi
-# Check whether --enable-sanitizer was given.
-if test ${enable_sanitizer+y}
+
+# Check whether --with-sanitizer was given.
+if test ${with_sanitizer+y}
then :
- enableval=$enable_sanitizer;
+ withval=$with_sanitizer;
fi
+if test "x$with_sanitizer" = x
+then :
+
+ with_sanitizer="address"
+
+elif test "$with_sanitizer" != address -a "$with_sanitizer" != leak -a "$with_sanitizer" != memory -a "$with_sanitizer" != no -a "$with_sanitizer" != thread -a "$with_sanitizer" != undefined
+then :
+
+ as_fn_error $? "Unsupported --with-sanitizer value \"$with_sanitizer\" specified." "$LINENO" 5
+
+fi
if test x$enable_debug = xyes
then :
@@ -5248,11 +5246,11 @@ WARNINGS=""
if test -n "$GCC"
then :
- if test x$enable_sanitizer = xyes
+ if test x$with_sanitizer != xno
then :
- # Use -fsanitize=address with debugging...
- OPTIM="$OPTIM -fsanitize=address"
+ # Use -fsanitize=FOO with debugging...
+ OPTIM="$OPTIM -fsanitize=$with_sanitizer"
elif echo "$CPPFLAGS $CFLAGS" | grep -q _FORTIFY_SOURCE
then :
diff --git a/configure.ac b/configure.ac
index f04930282..899e31c62 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,15 +46,6 @@ AS_IF([test "x$host_os_version" = x], [
dnl Determine whether we are cross-compiling...
-AS_IF([test "$build" = "$host"], [
- # No, build local targets
- LOCALTARGET="local"
-], [
- # Yes, don't build local targets
- LOCALTARGET=""
-])
-AC_SUBST([LOCALTARGET])
-
AC_PATH_PROGS([CODE_SIGN], [codesign true])
@@ -463,7 +454,12 @@ AC_SUBST([LIBCUPS_STATIC])
dnl Extra compiler options...
AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [turn on debugging, default=no]))
AC_ARG_ENABLE([maintainer], AS_HELP_STRING([--enable-maintainer], [turn on maintainer mode, default=no]))
-AC_ARG_ENABLE([sanitizer], AS_HELP_STRING([--enable-sanitizer], [build with AddressSanitizer, default=no]))
+AC_ARG_WITH([sanitizer], AS_HELP_STRING([--with-sanitizer], [build with address, leak, memory, thread, or undefined sanitizer, default=no]))
+AS_IF([test "x$with_sanitizer" = x], [
+ with_sanitizer="address"
+], [test "$with_sanitizer" != address -a "$with_sanitizer" != leak -a "$with_sanitizer" != memory -a "$with_sanitizer" != no -a "$with_sanitizer" != thread -a "$with_sanitizer" != undefined], [
+ AC_MSG_ERROR([Unsupported --with-sanitizer value "$with_sanitizer" specified.])
+])
AS_IF([test x$enable_debug = xyes], [
CSFLAGS=""
@@ -482,9 +478,9 @@ WARNINGS=""
AC_SUBST([WARNINGS])
AS_IF([test -n "$GCC"], [
- AS_IF([test x$enable_sanitizer = xyes], [
- # Use -fsanitize=address with debugging...
- OPTIM="$OPTIM -fsanitize=address"
+ AS_IF([test x$with_sanitizer != xno], [
+ # Use -fsanitize=FOO with debugging...
+ OPTIM="$OPTIM -fsanitize=$with_sanitizer"
], [echo "$CPPFLAGS $CFLAGS" | grep -q _FORTIFY_SOURCE], [
# Don't add _FORTIFY_SOURCE if it is already there
], [
diff --git a/cups/Makefile b/cups/Makefile
index 62d12b36a..5d260992b 100644
--- a/cups/Makefile
+++ b/cups/Makefile
@@ -74,6 +74,7 @@ TESTOBJS = \
testjson.o \
testjwt.o \
testoptions.o \
+ testpwg.o \
testraster.o \
testtestpage.o \
testthreads.o \
@@ -83,7 +84,6 @@ OBJS = \
$(TESTOBJS)
# testlang.o \
-# testpwg.o \
#
@@ -156,13 +156,13 @@ UNITTARGETS = \
testjson \
testjwt \
testoptions \
+ testpwg \
testraster \
testtestpage \
testthreads \
tlscheck
# testlang \
-# testpwg \
TARGETS = \
$(LIBTARGETS)
@@ -181,32 +181,50 @@ all: $(TARGETS)
test: $(UNITTARGETS)
rm -f test.log
+ date >test.log
echo Running array API tests...
./testarray 2>>test.log
# if test `uname` != Darwin; then \
# echo Running CUPS API tests...; \
# ./testcups 2>>test.log; \
# fi
+ echo ""
+ echo Running credentials API tests...
+ ./testcreds 2>>test.log
+ echo ""
echo Running file API tests...
./testfile 2>>test.log
+ echo ""
echo Running form API tests...
./testform 2>>test.log
+# echo ""
# echo Running cupsGetDests API tests...
# ./testgetdests 2>>test.log
+ echo ""
echo Running hash API tests...
./testhash 2>>test.log
+ echo ""
echo Running HTTP API tests...
./testhttp 2>>test.log
+ echo ""
echo Running IPP API tests...
./testipp 2>>test.log
+ echo ""
echo Running internationalization API tests...
./testi18n 2>>test.log
+ echo ""
echo Running JSON API tests...
./testjson 2>>test.log
+ echo ""
echo Running JWT API tests...
./testjwt 2>>test.log
+ echo ""
echo Running option API tests...
./testoptions 2>>test.log
+ echo ""
+ echo Running PWG API tests...
+ ./testpwg 2>>test.log
+ echo ""
echo Running raster API tests...
./testraster 2>>test.log
./testtestpage 2>>test.log
@@ -214,10 +232,18 @@ test: $(UNITTARGETS)
fuzz: $(UNITTARGETS)
echo Fuzzing IPP API...
./fuzzipp 2>fuzz.log
+ echo ""
echo Fuzzing JSON API...
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES=yes AFL_NO_UI=yes afl-fuzz -n -i json-afl-in -o json-afl-out -V 120 -- ./testjson 2>>fuzz.log
+#
+# Make unit tests...
+#
+
+unittests: $(UNITTARGETS)
+
+
#
# Remove object and target files...
#
@@ -572,8 +598,6 @@ testpwg: testpwg.o $(LIBCUPS_STATIC) test.ppd
echo Linking $@...
$(CC) $(LDFLAGS) $(OPTIM) -o $@ testpwg.o $(LIBCUPS_STATIC) $(LIBS)
$(CODE_SIGN) $(CSFLAGS) $@
- echo Running PWG API tests...
- ./testpwg test.ppd
#
@@ -624,7 +648,7 @@ doc:
echo "Generating CUPS Programming Manual..."
$(RM) cupspm.xml
codedoc --section "Programming" --body cupspm.md cupspm.xml \
- $(LIBOBJS:.o=.c) $(HEADERS) --coverimage cupspm.png \
+ $(LIBOBJS:.o=.c) tls-openssl.c $(HEADERS) --coverimage cupspm.png \
--epub ../doc/cupspm.epub
codedoc --section "Programming" --body cupspm.md \
cupspm.xml > ../doc/cupspm.html
diff --git a/cups/cups-private.h b/cups/cups-private.h
index 4584c531e..6822d017f 100644
--- a/cups/cups-private.h
+++ b/cups/cups-private.h
@@ -76,8 +76,8 @@ typedef struct _cups_globals_s /**** CUPS global state data ****/
{
/* Multiple places... */
const char *datadir, // Data directory (CUPS_DATADIR environment var)
- *sysconfig, // System config files (CUPS_SERVERROOT environment var)
- *userconfig; // User-specific config files (various environment vars)
+ *sysconfig; // System config files (CUPS_SERVERROOT environment var)
+ char *userconfig; // User-specific config files
#ifndef _WIN32
#define PW_BUF_SIZE 16384 /* As per glibc manual page */
char pw_buf[PW_BUF_SIZE];
diff --git a/cups/cups.h b/cups/cups.h
index 6450230ea..3fca693c8 100644
--- a/cups/cups.h
+++ b/cups/cups.h
@@ -42,44 +42,6 @@ extern "C" {
# define CUPS_LENGTH_VARIABLE (ssize_t)0
# define CUPS_TIMEOUT_DEFAULT 0
-typedef enum cups_whichjobs_e // Which jobs for @link cupsGetJobs@
-{
- CUPS_WHICHJOBS_ALL = -1, // All jobs
- CUPS_WHICHJOBS_ACTIVE, // Pending/held/processing jobs
- CUPS_WHICHJOBS_COMPLETED // Completed/canceled/aborted jobs
-} cups_whichjobs_t;
-
-// Flags for cupsConnectDest and cupsEnumDests
-# define CUPS_DEST_FLAGS_NONE 0x00
- // No flags are set
-# define CUPS_DEST_FLAGS_UNCONNECTED 0x01
- // There is no connection
-# define CUPS_DEST_FLAGS_MORE 0x02
- // There are more destinations
-# define CUPS_DEST_FLAGS_REMOVED 0x04
- // The destination has gone away
-# define CUPS_DEST_FLAGS_ERROR 0x08
- // An error occurred
-# define CUPS_DEST_FLAGS_RESOLVING 0x10
- // The destination address is being resolved
-# define CUPS_DEST_FLAGS_CONNECTING 0x20
- // A connection is being established
-# define CUPS_DEST_FLAGS_CANCELED 0x40
- // Operation was canceled
-# define CUPS_DEST_FLAGS_DEVICE 0x80
- // For @link cupsConnectDest@: Connect to device
-
-// Flags for cupsGetDestMediaByName/Size
-# define CUPS_MEDIA_FLAGS_DEFAULT 0x00
- // Find the closest size supported by the printer
-# define CUPS_MEDIA_FLAGS_BORDERLESS 0x01
- // Find a borderless size
-# define CUPS_MEDIA_FLAGS_DUPLEX 0x02
- // Find a size compatible with 2-sided printing
-# define CUPS_MEDIA_FLAGS_EXACT 0x04
- // Find an exact match for the size
-# define CUPS_MEDIA_FLAGS_READY 0x08
- // If the printer supports media sensing, find the size amongst the "ready" media.
// Options and values
# define CUPS_COPIES "copies"
@@ -148,8 +110,9 @@ typedef enum cups_whichjobs_e // Which jobs for @link cupsGetJobs@
# define CUPS_PRINT_COLOR_MODE_SUPPORTED "print-color-mode-supported"
# define CUPS_PRINT_COLOR_MODE_AUTO "auto"
-# define CUPS_PRINT_COLOR_MODE_MONOCHROME "monochrome"
+# define CUPS_PRINT_COLOR_MODE_BI_LEVEL "bi-level"
# define CUPS_PRINT_COLOR_MODE_COLOR "color"
+# define CUPS_PRINT_COLOR_MODE_MONOCHROME "monochrome"
# define CUPS_PRINT_QUALITY "print-quality"
# define CUPS_PRINT_QUALITY_SUPPORTED "print-quality-supported"
@@ -170,45 +133,116 @@ typedef enum cups_whichjobs_e // Which jobs for @link cupsGetJobs@
// Types and structures...
//
-typedef unsigned cups_ptype_t; // Printer type/capability bits
-enum cups_ptype_e // Printer type/capability bit constants
-{ // Not a typedef'd enum so we can OR
- CUPS_PRINTER_LOCAL = 0x0000, // Local printer or class
- CUPS_PRINTER_CLASS = 0x0001, // Printer class
- CUPS_PRINTER_REMOTE = 0x0002, // Remote printer or class
- CUPS_PRINTER_BW = 0x0004, // Can do B&W printing
- CUPS_PRINTER_COLOR = 0x0008, // Can do color printing
- CUPS_PRINTER_DUPLEX = 0x0010, // Can do two-sided printing
- CUPS_PRINTER_STAPLE = 0x0020, // Can staple output
- CUPS_PRINTER_COPIES = 0x0040, // Can do copies in hardware
- CUPS_PRINTER_COLLATE = 0x0080, // Can quickly collate copies
- CUPS_PRINTER_PUNCH = 0x0100, // Can punch output
- CUPS_PRINTER_COVER = 0x0200, // Can cover output
- CUPS_PRINTER_BIND = 0x0400, // Can bind output
- CUPS_PRINTER_SORT = 0x0800, // Can sort output
- CUPS_PRINTER_SMALL = 0x1000, // Can print on Letter/Legal/A4-size media
- CUPS_PRINTER_MEDIUM = 0x2000, // Can print on Tabloid/B/C/A3/A2-size media
- CUPS_PRINTER_LARGE = 0x4000, // Can print on D/E/A1/A0-size media
- CUPS_PRINTER_VARIABLE = 0x8000, // Can print on rolls and custom-size media
- CUPS_PRINTER_DEFAULT = 0x20000, // Default printer on network
- CUPS_PRINTER_FAX = 0x40000, // Fax queue
- CUPS_PRINTER_REJECTING = 0x80000, // Printer is rejecting jobs
- CUPS_PRINTER_NOT_SHARED = 0x200000, // Printer is not shared
- CUPS_PRINTER_AUTHENTICATED = 0x400000,// Printer requires authentication
- CUPS_PRINTER_COMMANDS = 0x800000, // Printer supports maintenance commands
- CUPS_PRINTER_DISCOVERED = 0x1000000, // Printer was discovered
- CUPS_PRINTER_SCANNER = 0x2000000, // Scanner-only device
- CUPS_PRINTER_MFP = 0x4000000, // Printer with scanning capabilities
- CUPS_PRINTER_OPTIONS = 0x6fffc // ~(CLASS | REMOTE | IMPLICIT | DEFAULT | FAX | REJECTING | DELETE | NOT_SHARED | AUTHENTICATED | COMMANDS | DISCOVERED) @private@
+enum cups_credpurpose_e // X.509 credential purposes
+{
+ CUPS_CREDPURPOSE_SERVER_AUTH = 0x01, // serverAuth
+ CUPS_CREDPURPOSE_CLIENT_AUTH = 0x02, // clientAuth
+ CUPS_CREDPURPOSE_CODE_SIGNING = 0x04, // codeSigning
+ CUPS_CREDPURPOSE_EMAIL_PROTECTION = 0x08, // emailProtection
+ CUPS_CREDPURPOSE_TIME_STAMPING = 0x10, // timeStamping
+ CUPS_CREDPURPOSE_OCSP_SIGNING = 0x20, // OCSPSigning
+ CUPS_CREDPURPOSE_ALL = 0x3f // All purposes
+};
+typedef unsigned cups_credpurpose_t; // Combined X.509 credential purposes for @link cupsCreateCredentials@ and @link cupsCreateCredentialsRequest@
+
+typedef enum cups_credtype_e // X.509 credential types for @link cupsCreateCredentials@ and @link cupsCreateCredentialsRequest@
+{
+ CUPS_CREDTYPE_DEFAULT, // Default type
+ CUPS_CREDTYPE_RSA_2048_SHA256, // RSA with 2048-bit keys and SHA-256 hash
+ CUPS_CREDTYPE_RSA_3072_SHA256, // RSA with 3072-bit keys and SHA-256 hash
+ CUPS_CREDTYPE_RSA_4096_SHA256, // RSA with 4096-bit keys and SHA-256 hash
+ CUPS_CREDTYPE_ECDSA_P256_SHA256, // ECDSA using the P-256 curve with SHA-256 hash
+ CUPS_CREDTYPE_ECDSA_P384_SHA256, // ECDSA using the P-384 curve with SHA-256 hash
+ CUPS_CREDTYPE_ECDSA_P521_SHA256 // ECDSA using the P-521 curve with SHA-256 hash
+} cups_credtype_t;
+
+enum cups_credusage_e // X.509 keyUsage flags
+{
+ CUPS_CREDUSAGE_DIGITAL_SIGNATURE = 0x001, // digitalSignature
+ CUPS_CREDUSAGE_NON_REPUDIATION = 0x002, // nonRepudiation/contentCommitment
+ CUPS_CREDUSAGE_KEY_ENCIPHERMENT = 0x004, // keyEncipherment
+ CUPS_CREDUSAGE_DATA_ENCIPHERMENT = 0x008, // dataEncipherment
+ CUPS_CREDUSAGE_KEY_AGREEMENT = 0x010, // keyAgreement
+ CUPS_CREDUSAGE_KEY_CERT_SIGN = 0x020, // keyCertSign
+ CUPS_CREDUSAGE_CRL_SIGN = 0x040, // cRLSign
+ CUPS_CREDUSAGE_ENCIPHER_ONLY = 0x080, // encipherOnly
+ CUPS_CREDUSAGE_DECIPHER_ONLY = 0x100, // decipherOnly
+ CUPS_CREDUSAGE_DEFAULT_CA = 0x061, // Defaults for CA certs
+ CUPS_CREDUSAGE_DEFAULT_TLS = 0x005, // Defaults for TLS certs
+ CUPS_CREDUSAGE_ALL = 0x1ff // All keyUsage flags
+};
+typedef unsigned cups_credusage_t; // Combined X.509 keyUsage flags for @link cupsCreateCredentials@ and @link cupsCreateCredentialsRequest@
+
+enum cups_dest_flags_e // Flags for @link cupsConnectDest@ and @link cupsEnumDests@
+{
+ CUPS_DEST_FLAGS_NONE = 0x00, // No flags are set
+ CUPS_DEST_FLAGS_UNCONNECTED = 0x01, // There is no connection
+ CUPS_DEST_FLAGS_MORE = 0x02, // There are more destinations
+ CUPS_DEST_FLAGS_REMOVED = 0x04, // The destination has gone away
+ CUPS_DEST_FLAGS_ERROR = 0x08, // An error occurred
+ CUPS_DEST_FLAGS_RESOLVING = 0x10, // The destination address is being resolved
+ CUPS_DEST_FLAGS_CONNECTING = 0x20, // A connection is being established
+ CUPS_DEST_FLAGS_CANCELED = 0x40, // Operation was canceled
+ CUPS_DEST_FLAGS_DEVICE = 0x80 // For @link cupsConnectDest@: Connect to device
+};
+typedef unsigned cups_dest_flags_t; // Combined flags for @link cupsConnectDest@ and @link cupsEnumDests@
+
+enum cups_media_flags_e // Flags for @link cupsGetDestMediaByName@ and @link cupsGetDestMediaBySize@
+{
+ CUPS_MEDIA_FLAGS_DEFAULT = 0x00, // Find the closest size supported by the printer
+ CUPS_MEDIA_FLAGS_BORDERLESS = 0x01, // Find a borderless size
+ CUPS_MEDIA_FLAGS_DUPLEX = 0x02, // Find a size compatible with 2-sided printing
+ CUPS_MEDIA_FLAGS_EXACT = 0x04, // Find an exact match for the size
+ CUPS_MEDIA_FLAGS_READY = 0x08 // If the printer supports media sensing, find the size amongst the "ready" media.
};
+typedef unsigned cups_media_flags_t; // Combined flags for @link cupsGetDestMediaByName@ and @link cupsGetDestMediaBySize@
+
+enum cups_ptype_e // Printer type/capability flags
+{
+ CUPS_PRINTER_LOCAL = 0x0000, // Local printer or class
+ CUPS_PRINTER_CLASS = 0x0001, // Printer class
+ CUPS_PRINTER_REMOTE = 0x0002, // Remote printer or class
+ CUPS_PRINTER_BW = 0x0004, // Can do B&W printing
+ CUPS_PRINTER_COLOR = 0x0008, // Can do color printing
+ CUPS_PRINTER_DUPLEX = 0x0010, // Can do two-sided printing
+ CUPS_PRINTER_STAPLE = 0x0020, // Can staple output
+ CUPS_PRINTER_COPIES = 0x0040, // Can do copies in hardware
+ CUPS_PRINTER_COLLATE = 0x0080, // Can quickly collate copies
+ CUPS_PRINTER_PUNCH = 0x0100, // Can punch output
+ CUPS_PRINTER_COVER = 0x0200, // Can cover output
+ CUPS_PRINTER_BIND = 0x0400, // Can bind output
+ CUPS_PRINTER_SORT = 0x0800, // Can sort output
+ CUPS_PRINTER_SMALL = 0x1000, // Can print on Letter/Legal/A4-size media
+ CUPS_PRINTER_MEDIUM = 0x2000, // Can print on Tabloid/B/C/A3/A2-size media
+ CUPS_PRINTER_LARGE = 0x4000, // Can print on D/E/A1/A0-size media
+ CUPS_PRINTER_VARIABLE = 0x8000, // Can print on rolls and custom-size media
+ CUPS_PRINTER_DEFAULT = 0x20000, // Default printer on network
+ CUPS_PRINTER_FAX = 0x40000, // Fax queue
+ CUPS_PRINTER_REJECTING = 0x80000, // Printer is rejecting jobs
+ CUPS_PRINTER_NOT_SHARED = 0x200000, // Printer is not shared
+ CUPS_PRINTER_AUTHENTICATED = 0x400000, // Printer requires authentication
+ CUPS_PRINTER_COMMANDS = 0x800000, // Printer supports maintenance commands
+ CUPS_PRINTER_DISCOVERED = 0x1000000, // Printer was discovered
+ CUPS_PRINTER_SCANNER = 0x2000000, // Scanner-only device
+ CUPS_PRINTER_MFP = 0x4000000, // Printer with scanning capabilities
+ CUPS_PRINTER_OPTIONS = 0x6fffc // ~(CLASS | REMOTE | IMPLICIT | DEFAULT | FAX | REJECTING | DELETE | NOT_SHARED | AUTHENTICATED | COMMANDS | DISCOVERED) @private@
+};
+typedef unsigned cups_ptype_t; // Combined printer type/capability flags
+
+typedef enum cups_whichjobs_e // Which jobs for @link cupsGetJobs@
+{
+ CUPS_WHICHJOBS_ALL = -1, // All jobs
+ CUPS_WHICHJOBS_ACTIVE, // Pending/held/processing jobs
+ CUPS_WHICHJOBS_COMPLETED // Completed/canceled/aborted jobs
+} cups_whichjobs_t;
-typedef struct cups_option_s //// Printer Options
+typedef struct cups_option_s // Printer Options
{
char *name; // Name of option
char *value; // Value of option
} cups_option_t;
-typedef struct cups_dest_s //// Destination
+typedef struct cups_dest_s // Destination
{
char *name, // Printer or class name
*instance; // Local instance name or NULL
@@ -235,7 +269,7 @@ typedef struct cups_job_s // Job
time_t processing_time; // Time the job was processed
} cups_job_t;
-typedef struct cups_size_s //// Media Size
+typedef struct cups_size_s // Media information
{
char media[128], // Media name to use
color[128], // Media color (blank for any/auto)
@@ -249,10 +283,13 @@ typedef struct cups_size_s //// Media Size
top; // Top margin in hundredths of millimeters
} cups_size_t;
+typedef bool (*cups_cert_san_cb_t)(const char *common_name, const char *subject_alt_name, void *user_data);
+ // Certificate signing subjectAltName callback
+
typedef bool (*cups_client_cert_cb_t)(http_t *http, void *tls, cups_array_t *distinguished_names, void *user_data);
// Client credentials callback
-typedef bool (*cups_dest_cb_t)(void *user_data, unsigned flags, cups_dest_t *dest);
+typedef bool (*cups_dest_cb_t)(void *user_data, cups_dest_flags_t flags, cups_dest_t *dest);
// Destination enumeration callback
typedef const char *(*cups_oauth_cb_t)(http_t *http, const char *realm, const char *scope, const char *resource, void *user_data);
@@ -279,10 +316,15 @@ extern bool cupsCheckDestSupported(http_t *http, cups_dest_t *dest, cups_dinfo_
extern ipp_status_t cupsCloseDestJob(http_t *http, cups_dest_t *dest, cups_dinfo_t *info, int job_id) _CUPS_PUBLIC;
extern size_t cupsConcatString(char *dst, const char *src, size_t dstsize) _CUPS_PUBLIC;
extern http_t *cupsConnectDest(cups_dest_t *dest, unsigned flags, int msec, int *cancel, char *resource, size_t resourcesize, cups_dest_cb_t cb, void *user_data) _CUPS_PUBLIC;
+extern char *cupsCopyCredentials(const char *path, const char *common_name) _CUPS_PUBLIC;
+extern char *cupsCopyCredentialsKey(const char *path, const char *common_name) _CUPS_PUBLIC;
+extern char *cupsCopyCredentialsRequest(const char *path, const char *common_name) _CUPS_PUBLIC;
extern size_t cupsCopyDest(cups_dest_t *dest, size_t num_dests, cups_dest_t **dests) _CUPS_PUBLIC;
extern cups_dinfo_t *cupsCopyDestInfo(http_t *http, cups_dest_t *dest) _CUPS_PUBLIC;
extern int cupsCopyDestConflicts(http_t *http, cups_dest_t *dest, cups_dinfo_t *info, size_t num_options, cups_option_t *options, const char *new_option, const char *new_value, size_t *num_conflicts, cups_option_t **conflicts, size_t *num_resolved, cups_option_t **resolved) _CUPS_PUBLIC;
extern size_t cupsCopyString(char *dst, const char *src, size_t dstsize) _CUPS_PUBLIC;
+extern bool cupsCreateCredentials(const char *path, bool ca_cert, cups_credpurpose_t purpose, cups_credtype_t type, cups_credusage_t usage, const char *organization, const char *org_unit, const char *locality, const char *state_province, const char *country, const char *common_name, size_t num_alt_names, const char * const *alt_names, const char *root_name, time_t expiration_date) _CUPS_PUBLIC;
+extern bool cupsCreateCredentialsRequest(const char *path, cups_credpurpose_t purpose, cups_credtype_t type, cups_credusage_t usage, const char *organization, const char *org_unit, const char *locality, const char *state_province, const char *country, const char *common_name, size_t num_alt_names, const char * const *alt_names) _CUPS_PUBLIC;
extern ipp_status_t cupsCreateDestJob(http_t *http, cups_dest_t *dest, cups_dinfo_t *info, int *job_id, const char *title, size_t num_options, cups_option_t *options) _CUPS_PUBLIC;
extern int cupsDoAuthentication(http_t *http, const char *method, const char *resource) _CUPS_PUBLIC;
@@ -336,8 +378,6 @@ extern const char *cupsLocalizeDestMedia(http_t *http, cups_dest_t *dest, cups_d
extern const char *cupsLocalizeDestOption(http_t *http, cups_dest_t *dest, cups_dinfo_t *info, const char *option) _CUPS_PUBLIC;
extern const char *cupsLocalizeDestValue(http_t *http, cups_dest_t *dest, cups_dinfo_t *info, const char *option, const char *value) _CUPS_PUBLIC;
-extern int cupsMakeServerCredentials(const char *path, const char *common_name, int num_alt_names, const char **alt_names, time_t expiration_date) _CUPS_PUBLIC;
-
extern char *cupsNotifySubject(cups_lang_t *lang, ipp_t *event) _CUPS_PUBLIC;
extern char *cupsNotifyText(cups_lang_t *lang, ipp_t *event) _CUPS_PUBLIC;
@@ -349,6 +389,7 @@ extern ssize_t cupsReadResponseData(http_t *http, char *buffer, size_t length)
extern size_t cupsRemoveDest(const char *name, const char *instance, size_t num_dests, cups_dest_t **dests) _CUPS_PUBLIC;
extern size_t cupsRemoveOption(const char *name, size_t num_options, cups_option_t **options) _CUPS_PUBLIC;
+extern bool cupsSaveCredentials(const char *path, const char *common_name, const char *credentials, const char *key) _CUPS_PUBLIC;
extern http_status_t cupsSendRequest(http_t *http, ipp_t *request, const char *resource, size_t length) _CUPS_PUBLIC;
extern void cupsSetOAuthCB(cups_oauth_cb_t cb, void *data) _CUPS_PUBLIC;
extern void cupsSetClientCertCB(cups_client_cert_cb_t cb, void *user_data) _CUPS_PUBLIC;
@@ -359,9 +400,10 @@ extern void cupsSetEncryption(http_encryption_t e) _CUPS_PUBLIC;
extern void cupsSetPasswordCB(cups_password_cb_t cb, void *user_data) _CUPS_PUBLIC;
extern void cupsSetServer(const char *server) _CUPS_PUBLIC;
extern void cupsSetServerCertCB(cups_server_cert_cb_t cb, void *user_data) _CUPS_PUBLIC;
-extern int cupsSetServerCredentials(const char *path, const char *common_name, int auto_create) _CUPS_PUBLIC;
+extern bool cupsSetServerCredentials(const char *path, const char *common_name, bool auto_create) _CUPS_PUBLIC;
extern void cupsSetUser(const char *user) _CUPS_PUBLIC;
extern void cupsSetUserAgent(const char *user_agent) _CUPS_PUBLIC;
+extern bool cupsSignCredentialsRequest(const char *path, const char *common_name, const char *request, const char *root_name, cups_credpurpose_t allowed_purpose, cups_credusage_t allowed_usage, cups_cert_san_cb_t cb, void *cb_data, time_t expiration_date) _CUPS_PUBLIC;
extern http_status_t cupsStartDestDocument(http_t *http, cups_dest_t *dest, cups_dinfo_t *info, int job_id, const char *docname, const char *format, size_t num_options, cups_option_t *options, bool last_document) _CUPS_PUBLIC;
extern int cupsTempFd(const char *prefix, const char *suffix, char *filename, size_t len) _CUPS_PUBLIC;
extern cups_file_t *cupsTempFile(const char *prefix, const char *suffix, char *filename, size_t len) _CUPS_PUBLIC;
diff --git a/cups/debug.c b/cups/debug.c
index 005ce8a3f..4b18b367b 100644
--- a/cups/debug.c
+++ b/cups/debug.c
@@ -1,16 +1,16 @@
-/*
- * Debugging functions for CUPS.
- *
- * Copyright © 2022 by OpenPrinting.
- * Copyright © 2008-2018 by Apple Inc.
- *
- * Licensed under Apache License v2.0. See the file "LICENSE" for more
- * information.
- */
-
-/*
- * Include necessary headers...
- */
+//
+// Debugging functions for CUPS.
+//
+// Copyright © 2022-2023 by OpenPrinting.
+// Copyright © 2008-2018 by Apple Inc.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+//
+// Include necessary headers...
+//
#include "cups-private.h"
#include "debug-internal.h"
@@ -20,11 +20,11 @@
# include
# include
# define getpid (int)GetCurrentProcessId
-int /* O - 0 on success, -1 on failure */
-_cups_gettimeofday(struct timeval *tv, /* I - Timeval struct */
- void *tz) /* I - Timezone */
+int // O - 0 on success, -1 on failure
+_cups_gettimeofday(struct timeval *tv, // I - Timeval struct
+ void *tz) // I - Timezone
{
- struct _timeb timebuffer; /* Time buffer struct */
+ struct _timeb timebuffer; // Time buffer struct
_ftime(&timebuffer);
tv->tv_sec = (long)timebuffer.time;
tv->tv_usec = timebuffer.millitm * 1000;
@@ -33,79 +33,72 @@ _cups_gettimeofday(struct timeval *tv, /* I - Timeval struct */
#else
# include
# include
-#endif /* _WIN32 */
+#endif // _WIN32
#include
#include
#ifdef DEBUG
-/*
- * Globals...
- */
+//
+// Globals...
+//
int _cups_debug_fd = -1;
- /* Debug log file descriptor */
+ // Debug log file descriptor
int _cups_debug_level = 1;
- /* Log level (0 to 9) */
+ // Log level (0 to 9)
-/*
- * Local globals...
- */
+//
+// Local globals...
+//
static regex_t *debug_filter = NULL;
- /* Filter expression for messages */
-static int debug_init = 0; /* Did we initialize debugging? */
+ // Filter expression for messages
+static int debug_init = 0; // Did we initialize debugging?
static cups_mutex_t debug_init_mutex = CUPS_MUTEX_INITIALIZER,
- /* Mutex to control initialization */
+ // Mutex to control initialization
debug_log_mutex = CUPS_MUTEX_INITIALIZER;
- /* Mutex to serialize log entries */
+ // Mutex to serialize log entries
-/*
- * 'debug_thread_id()' - Return an integer representing the current thread.
- */
+//
+// 'debug_thread_id()' - Return an integer representing the current thread.
+//
-static int /* O - Local thread ID */
+static int // O - Local thread ID
debug_thread_id(void)
{
- _cups_globals_t *cg = _cupsGlobals(); /* Global data */
+ _cups_globals_t *cg = _cupsGlobals(); // Global data
return (cg->thread_id);
}
-/*
- * '_cups_debug_printf()' - Write a formatted line to the log.
- */
+//
+// '_cups_debug_printf()' - Write a formatted line to the log.
+//
void
-_cups_debug_printf(const char *format, /* I - Printf-style format string */
- ...) /* I - Additional arguments as needed */
+_cups_debug_printf(const char *format, // I - Printf-style format string
+ ...) // I - Additional arguments as needed
{
- va_list ap; /* Pointer to arguments */
- struct timeval curtime; /* Current time */
- char buffer[2048]; /* Output buffer */
- ssize_t bytes; /* Number of bytes in buffer */
- int level; /* Log level in message */
+ va_list ap; // Pointer to arguments
+ struct timeval curtime; // Current time
+ char buffer[2048]; // Output buffer
+ ssize_t bytes; // Number of bytes in buffer
+ int level; // Log level in message
- /*
- * See if we need to do any logging...
- */
-
+ // See if we need to do any logging...
if (!debug_init)
- _cups_debug_set(getenv("CUPS_DEBUG_LOG"), getenv("CUPS_DEBUG_LEVEL"),
- getenv("CUPS_DEBUG_FILTER"), 0);
+ _cups_debug_set(getenv("CUPS_DEBUG_LOG"), getenv("CUPS_DEBUG_LEVEL"), getenv("CUPS_DEBUG_FILTER"), 0);
if (_cups_debug_fd < 0)
return;
- /*
- * Filter as needed...
- */
-
+ // Filter as needed...
if (isdigit(format[0]))
level = *format++ - '0';
else
@@ -116,7 +109,7 @@ _cups_debug_printf(const char *format, /* I - Printf-style format string */
if (debug_filter)
{
- int result; /* Filter result */
+ int result; // Filter result
cupsMutexLock(&debug_init_mutex);
result = regexec(debug_filter, format, 0, NULL, 0);
@@ -126,15 +119,9 @@ _cups_debug_printf(const char *format, /* I - Printf-style format string */
return;
}
- /*
- * Format the message...
- */
-
+ // Format the message...
gettimeofday(&curtime, NULL);
- snprintf(buffer, sizeof(buffer), "T%03d %02d:%02d:%02d.%03d ",
- debug_thread_id(), (int)((curtime.tv_sec / 3600) % 24),
- (int)((curtime.tv_sec / 60) % 60),
- (int)(curtime.tv_sec % 60), (int)(curtime.tv_usec / 1000));
+ snprintf(buffer, sizeof(buffer), "T%03d %02d:%02d:%02d.%03d ", debug_thread_id(), (int)((curtime.tv_sec / 3600) % 24), (int)((curtime.tv_sec / 60) % 60), (int)(curtime.tv_sec % 60), (int)(curtime.tv_usec / 1000));
va_start(ap, format);
bytes = _cups_safe_vsnprintf(buffer + 19, sizeof(buffer) - 20, format, ap) + 19;
@@ -148,47 +135,36 @@ _cups_debug_printf(const char *format, /* I - Printf-style format string */
else if (buffer[bytes - 1] != '\n')
{
buffer[bytes++] = '\n';
- buffer[bytes] = '\0';
}
- /*
- * Write it out...
- */
-
+ // Write it out...
cupsMutexLock(&debug_log_mutex);
write(_cups_debug_fd, buffer, (size_t)bytes);
cupsMutexUnlock(&debug_log_mutex);
}
-/*
- * '_cups_debug_puts()' - Write a single line to the log.
- */
+//
+// '_cups_debug_puts()' - Write a single line to the log.
+//
void
-_cups_debug_puts(const char *s) /* I - String to output */
+_cups_debug_puts(const char *s) // I - String to output
{
- struct timeval curtime; /* Current time */
- char buffer[2048]; /* Output buffer */
- ssize_t bytes; /* Number of bytes in buffer */
- int level; /* Log level in message */
+ struct timeval curtime; // Current time
+ char buffer[2048]; // Output buffer
+ ssize_t bytes; // Number of bytes in buffer
+ int level; // Log level in message
- /*
- * See if we need to do any logging...
- */
-
+ // See if we need to do any logging...
if (!debug_init)
- _cups_debug_set(getenv("CUPS_DEBUG_LOG"), getenv("CUPS_DEBUG_LEVEL"),
- getenv("CUPS_DEBUG_FILTER"), 0);
+ _cups_debug_set(getenv("CUPS_DEBUG_LOG"), getenv("CUPS_DEBUG_LEVEL"), getenv("CUPS_DEBUG_FILTER"), 0);
if (_cups_debug_fd < 0)
return;
- /*
- * Filter as needed...
- */
-
+ // Filter as needed...
if (isdigit(s[0]))
level = *s++ - '0';
else
@@ -199,7 +175,7 @@ _cups_debug_puts(const char *s) /* I - String to output */
if (debug_filter)
{
- int result; /* Filter result */
+ int result; // Filter result
cupsMutexLock(&debug_init_mutex);
result = regexec(debug_filter, s, 0, NULL, 0);
@@ -209,16 +185,9 @@ _cups_debug_puts(const char *s) /* I - String to output */
return;
}
- /*
- * Format the message...
- */
-
+ // Format the message...
gettimeofday(&curtime, NULL);
- bytes = snprintf(buffer, sizeof(buffer), "T%03d %02d:%02d:%02d.%03d %s",
- debug_thread_id(), (int)((curtime.tv_sec / 3600) % 24),
- (int)((curtime.tv_sec / 60) % 60),
- (int)(curtime.tv_sec % 60), (int)(curtime.tv_usec / 1000),
- s);
+ bytes = snprintf(buffer, sizeof(buffer), "T%03d %02d:%02d:%02d.%03d %s", debug_thread_id(), (int)((curtime.tv_sec / 3600) % 24), (int)((curtime.tv_sec / 60) % 60), (int)(curtime.tv_sec % 60), (int)(curtime.tv_usec / 1000), s);
if ((size_t)bytes >= (sizeof(buffer) - 1))
{
@@ -228,37 +197,30 @@ _cups_debug_puts(const char *s) /* I - String to output */
else if (buffer[bytes - 1] != '\n')
{
buffer[bytes++] = '\n';
- buffer[bytes] = '\0';
}
- /*
- * Write it out...
- */
-
+ // Write it out...
cupsMutexLock(&debug_log_mutex);
write(_cups_debug_fd, buffer, (size_t)bytes);
cupsMutexUnlock(&debug_log_mutex);
}
-/*
- * '_cups_debug_set()' - Enable or disable debug logging.
- */
+//
+// '_cups_debug_set()' - Enable or disable debug logging.
+//
void
-_cups_debug_set(const char *logfile, /* I - Log file or NULL */
- const char *level, /* I - Log level or NULL */
- const char *filter, /* I - Filter string or NULL */
- int force) /* I - Force initialization */
+_cups_debug_set(const char *logfile, // I - Log file or NULL
+ const char *level, // I - Log level or NULL
+ const char *filter, // I - Filter string or NULL
+ int force) // I - Force initialization
{
cupsMutexLock(&debug_init_mutex);
if (!debug_init || force)
{
- /*
- * Restore debug settings to defaults...
- */
-
+ // Restore debug settings to defaults...
if (_cups_debug_fd != -1)
{
close(_cups_debug_fd);
@@ -273,17 +235,18 @@ _cups_debug_set(const char *logfile, /* I - Log file or NULL */
_cups_debug_level = 1;
- /*
- * Open logs, set log levels, etc.
- */
-
+ // Open logs, set log levels, etc.
if (!logfile)
+ {
_cups_debug_fd = -1;
+ }
else if (!strcmp(logfile, "-"))
+ {
_cups_debug_fd = 2;
+ }
else
{
- char buffer[1024]; /* Filename buffer */
+ char buffer[1024]; // Filename buffer
snprintf(buffer, sizeof(buffer), logfile, getpid());
@@ -299,12 +262,12 @@ _cups_debug_set(const char *logfile, /* I - Log file or NULL */
if (filter)
{
if ((debug_filter = (regex_t *)calloc(1, sizeof(regex_t))) == NULL)
- fputs("Unable to allocate memory for CUPS_DEBUG_FILTER - results not "
- "filtered!\n", stderr);
+ {
+ fputs("Unable to allocate memory for CUPS_DEBUG_FILTER - results not filtered.\n", stderr);
+ }
else if (regcomp(debug_filter, filter, REG_EXTENDED))
{
- fputs("Bad regular expression in CUPS_DEBUG_FILTER - results not "
- "filtered!\n", stderr);
+ fputs("Bad regular expression in CUPS_DEBUG_FILTER - results not filtered.\n", stderr);
free(debug_filter);
debug_filter = NULL;
}
@@ -318,56 +281,53 @@ _cups_debug_set(const char *logfile, /* I - Log file or NULL */
#else
-/*
- * '_cups_debug_set()' - Enable or disable debug logging.
- */
+//
+// '_cups_debug_set()' - Enable or disable debug logging.
+//
void
-_cups_debug_set(const char *logfile, /* I - Log file or NULL */
- const char *level, /* I - Log level or NULL */
- const char *filter, /* I - Filter string or NULL */
- int force) /* I - Force initialization */
+_cups_debug_set(const char *logfile, // I - Log file or NULL
+ const char *level, // I - Log level or NULL
+ const char *filter, // I - Filter string or NULL
+ int force) // I - Force initialization
{
(void)logfile;
(void)level;
(void)filter;
(void)force;
}
-#endif /* DEBUG */
+#endif // DEBUG
-/*
- * '_cups_safe_vsnprintf()' - Format a string into a fixed size buffer,
- * quoting special characters.
- */
+//
+// '_cups_safe_vsnprintf()' - Format a string into a fixed size buffer,
+// quoting special characters.
+//
-ssize_t /* O - Number of bytes formatted */
+ssize_t // O - Number of bytes formatted
_cups_safe_vsnprintf(
- char *buffer, /* O - Output buffer */
- size_t bufsize, /* O - Size of output buffer */
- const char *format, /* I - printf-style format string */
- va_list ap) /* I - Pointer to additional arguments */
+ char *buffer, // O - Output buffer
+ size_t bufsize, // O - Size of output buffer
+ const char *format, // I - printf-style format string
+ va_list ap) // I - Pointer to additional arguments
{
- char *bufptr, /* Pointer to position in buffer */
- *bufend, /* Pointer to end of buffer */
- size, /* Size character (h, l, L) */
- type; /* Format type character */
- int width, /* Width of field */
- prec; /* Number of characters of precision */
- char tformat[100], /* Temporary format string for snprintf() */
- *tptr, /* Pointer into temporary format */
- temp[1024]; /* Buffer for formatted numbers */
- char *s; /* Pointer to string */
- ssize_t bytes; /* Total number of bytes needed */
+ char *bufptr, // Pointer to position in buffer
+ *bufend, // Pointer to end of buffer
+ size, // Size character (h, l, L)
+ type; // Format type character
+ int width, // Width of field
+ prec; // Number of characters of precision
+ char tformat[100], // Temporary format string for snprintf()
+ *tptr, // Pointer into temporary format
+ temp[1024]; // Buffer for formatted numbers
+ char *s; // Pointer to string
+ ssize_t bytes; // Total number of bytes needed
if (!buffer || bufsize < 2 || !format)
return (-1);
- /*
- * Loop through the format string, formatting as needed...
- */
-
+ // Loop through the format string, formatting as needed...
bufptr = buffer;
bufend = buffer + bufsize - 1;
bytes = 0;
@@ -381,21 +341,20 @@ _cups_safe_vsnprintf(
if (*format == '%')
{
- if (bufptr < bufend)
+ if (bufptr && bufptr < bufend)
*bufptr++ = *format;
bytes ++;
format ++;
continue;
}
else if (strchr(" -+#\'", *format))
+ {
*tptr++ = *format++;
+ }
if (*format == '*')
{
- /*
- * Get width from argument...
- */
-
+ // Get width from argument...
format ++;
width = va_arg(ap, int);
@@ -424,10 +383,7 @@ _cups_safe_vsnprintf(
if (*format == '*')
{
- /*
- * Get precision from argument...
- */
-
+ // Get precision from argument...
format ++;
prec = va_arg(ap, int);
@@ -468,7 +424,9 @@ _cups_safe_vsnprintf(
size = *format++;
}
else
+ {
size = 0;
+ }
if (!*format)
break;
@@ -481,7 +439,7 @@ _cups_safe_vsnprintf(
switch (type)
{
- case 'E' : /* Floating point formats */
+ case 'E' : // Floating point formats
case 'G' :
case 'e' :
case 'f' :
@@ -493,14 +451,14 @@ _cups_safe_vsnprintf(
bytes += (int)strlen(temp);
- if (bufptr)
+ if (bufptr && bufptr < bufend)
{
cupsCopyString(bufptr, temp, (size_t)(bufend - bufptr));
bufptr += strlen(bufptr);
}
break;
- case 'B' : /* Integer formats */
+ case 'B' : // Integer formats
case 'X' :
case 'b' :
case 'd' :
@@ -515,7 +473,7 @@ _cups_safe_vsnprintf(
if (size == 'L')
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long long));
else
-# endif /* HAVE_LONG_LONG */
+# endif // HAVE_LONG_LONG
if (size == 'l')
snprintf(temp, sizeof(temp), tformat, va_arg(ap, long));
else
@@ -523,14 +481,14 @@ _cups_safe_vsnprintf(
bytes += (int)strlen(temp);
- if (bufptr)
+ if (bufptr && bufptr < bufend)
{
cupsCopyString(bufptr, temp, (size_t)(bufend - bufptr));
bufptr += strlen(bufptr);
}
break;
- case 'p' : /* Pointer value */
+ case 'p' : // Pointer value
if ((size_t)(width + 2) > sizeof(temp))
break;
@@ -538,20 +496,22 @@ _cups_safe_vsnprintf(
bytes += (int)strlen(temp);
- if (bufptr)
+ if (bufptr && bufptr < bufend)
{
cupsCopyString(bufptr, temp, (size_t)(bufend - bufptr));
bufptr += strlen(bufptr);
}
break;
- case 'c' : /* Character or character array */
+ case 'c' : // Character or character array
bytes += width;
- if (bufptr)
+ if (bufptr && bufptr < bufend)
{
if (width <= 1)
+ {
*bufptr++ = (char)va_arg(ap, int);
+ }
else
{
if ((bufptr + width) > bufend)
@@ -563,15 +523,17 @@ _cups_safe_vsnprintf(
}
break;
- case 's' : /* String */
+ case 's' : // String
if ((s = va_arg(ap, char *)) == NULL)
s = "(null)";
- /*
- * Copy the C string, replacing control chars and \ with
- * C character escapes...
- */
+ if (!bufptr)
+ {
+ bytes += 2 * strlen(s);
+ break;
+ }
+ // Copy the C string, replacing control chars and \ with C character escapes...
for (bufend --; *s && bufptr < bufend; s ++)
{
if (*s == '\n')
@@ -631,7 +593,7 @@ _cups_safe_vsnprintf(
bufend ++;
break;
- case 'n' : /* Output number of chars so far */
+ case 'n' : // Output number of chars so far
*(va_arg(ap, int *)) = (int)bytes;
break;
}
@@ -640,18 +602,16 @@ _cups_safe_vsnprintf(
{
bytes ++;
- if (bufptr < bufend)
+ if (bufptr && bufptr < bufend)
*bufptr++ = *format;
format ++;
}
}
- /*
- * Nul-terminate the string and return the number of characters needed.
- */
-
- *bufptr = '\0';
+ // Nul-terminate the string and return the number of characters needed.
+ if (bufptr)
+ *bufptr = '\0';
return (bytes);
}
diff --git a/cups/globals.c b/cups/globals.c
index f9400ad0a..949120962 100644
--- a/cups/globals.c
+++ b/cups/globals.c
@@ -360,7 +360,8 @@ cups_globals_alloc(void)
}
# endif // __APPLE__
- cg->userconfig = _cupsStrAlloc(temp);
+ // Can't use _cupsStrAlloc since it causes a loop with debug logging enabled
+ cg->userconfig = strdup(temp);
#endif // _WIN32
return (cg);
@@ -393,14 +394,13 @@ cups_globals_free(_cups_globals_t *cg) // I - Pointer to global data
httpClose(cg->http);
-#ifdef HAVE_TLS
_httpFreeCredentials(cg->tls_credentials);
-#endif // HAVE_TLS
cupsFileClose(cg->stdio_files[0]);
cupsFileClose(cg->stdio_files[1]);
cupsFileClose(cg->stdio_files[2]);
+ free(cg->userconfig);
free(cg->raster_error.start);
free(cg);
}
diff --git a/cups/http.h b/cups/http.h
index 8f9dfbc9b..559e52aff 100644
--- a/cups/http.h
+++ b/cups/http.h
@@ -458,9 +458,9 @@ extern void httpClose(http_t *http) _CUPS_PUBLIC;
extern void httpClearCookie(http_t *http) _CUPS_PUBLIC;
extern bool httpCompareCredentials(cups_array_t *cred1, cups_array_t *cred2) _CUPS_PUBLIC;
extern http_t *httpConnect(const char *host, int port, http_addrlist_t *addrlist, int family, http_encryption_t encryption, bool blocking, int msec, int *cancel) _CUPS_PUBLIC;
-extern int httpCopyCredentials(http_t *http, cups_array_t **credentials) _CUPS_PUBLIC;
+extern bool httpCopyCredentials(http_t *http, cups_array_t **credentials) _CUPS_PUBLIC;
extern cups_array_t *httpCreateCredentials(const void *data, size_t datalen) _CUPS_PUBLIC;
-extern int httpCredentialsAreValidForName(cups_array_t *credentials, const char *common_name);
+extern bool httpCredentialsAreValidForName(cups_array_t *credentials, const char *common_name);
extern time_t httpCredentialsGetExpiration(cups_array_t *credentials) _CUPS_PUBLIC;
extern http_trust_t httpCredentialsGetTrust(cups_array_t *credentials, const char *common_name) _CUPS_PUBLIC;
extern size_t httpCredentialsString(cups_array_t *credentials, char *buffer, size_t bufsize) _CUPS_PUBLIC;
diff --git a/cups/pwg-media.c b/cups/pwg-media.c
index 4d329b606..42cb91d35 100644
--- a/cups/pwg-media.c
+++ b/cups/pwg-media.c
@@ -1,7 +1,7 @@
/*
* PWG media name API implementation for CUPS.
*
- * Copyright © 2022 by OpenPrinting.
+ * Copyright © 2022-2023 by OpenPrinting.
* Copyright © 2009-2019 by Apple Inc.
*
* Licensed under Apache License v2.0. See the file "LICENSE" for more
@@ -970,16 +970,18 @@ pwg_format_inches(char *buf, // I - Buffer
size_t bufsize, // I - Size of buffer
int val) // I - Value in hundredths of millimeters
{
- int thousandths, // Thousandths of inches
- integer, // Integer portion
+ int integer, // Integer portion
fraction; // Fractional portion
- // Convert hundredths of millimeters to thousandths of inches and round to
- // the nearest thousandth.
- thousandths = (val * 1000 + 1270) / 2540;
- integer = thousandths / 1000;
- fraction = thousandths % 1000;
+ // Convert hundredths of millimeters to inches and thousandths.
+ integer = val / 2540;
+ fraction = ((val % 2540) * 1000 + 1270) / 2540;
+ if (fraction >= 1000)
+ {
+ integer ++;
+ fraction -= 1000;
+ }
// Format as a pair of integers (avoids locale stuff), avoiding trailing zeros...
if (fraction == 0)
diff --git a/cups/string.c b/cups/string.c
index c72346544..02c9b2c4b 100644
--- a/cups/string.c
+++ b/cups/string.c
@@ -1,17 +1,17 @@
-/*
- * String functions for CUPS.
- *
- * Copyright © 2022 by OpenPrinting.
- * Copyright © 2007-2019 by Apple Inc.
- * Copyright © 1997-2007 by Easy Software Products.
- *
- * Licensed under Apache License v2.0. See the file "LICENSE" for more
- * information.
- */
-
-/*
- * Include necessary headers...
- */
+//
+// String functions for CUPS.
+//
+// Copyright © 2022-2023 by OpenPrinting.
+// Copyright © 2007-2019 by Apple Inc.
+// Copyright © 1997-2007 by Easy Software Products.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+//
+// Include necessary headers...
+//
#define _CUPS_STRING_C_
#include "cups-private.h"
@@ -20,46 +20,40 @@
#include
-/*
- * Local globals...
- */
+//
+// Local globals...
+//
static cups_mutex_t sp_mutex = CUPS_MUTEX_INITIALIZER;
- /* Mutex to control access to pool */
+ // Mutex to control access to pool
static cups_array_t *stringpool = NULL;
- /* Global string pool */
+ // Global string pool
-/*
- * Local functions...
- */
+//
+// Local functions...
+//
static int compare_sp_items(_cups_sp_item_t *a, _cups_sp_item_t *b);
-/*
- * '_cupsStrAlloc()' - Allocate/reference a string.
- */
+//
+// '_cupsStrAlloc()' - Allocate/reference a string.
+//
-char * /* O - String pointer */
-_cupsStrAlloc(const char *s) /* I - String */
+char * // O - String pointer
+_cupsStrAlloc(const char *s) // I - String
{
- size_t slen; /* Length of string */
- _cups_sp_item_t *item, /* String pool item */
- *key; /* Search key */
+ size_t slen; // Length of string
+ _cups_sp_item_t *item, // String pool item
+ *key; // Search key
- /*
- * Range check input...
- */
-
+ // Range check input...
if (!s)
return (NULL);
- /*
- * Get the string pool...
- */
-
+ // Get the string pool...
cupsMutexLock(&sp_mutex);
if (!stringpool)
@@ -72,18 +66,12 @@ _cupsStrAlloc(const char *s) /* I - String */
return (NULL);
}
- /*
- * See if the string is already in the pool...
- */
-
+ // See if the string is already in the pool...
key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL)
{
- /*
- * Found it, return the cached string...
- */
-
+ // Found it, return the cached string...
item->ref_count ++;
#ifdef DEBUG_GUARDS
@@ -91,17 +79,14 @@ _cupsStrAlloc(const char *s) /* I - String */
if (item->guard != _CUPS_STR_GUARD)
abort();
-#endif /* DEBUG_GUARDS */
+#endif // DEBUG_GUARDS
cupsMutexUnlock(&sp_mutex);
return (item->str);
}
- /*
- * Not found, so allocate a new one...
- */
-
+ // Not found, so allocate a new one...
slen = strlen(s);
item = (_cups_sp_item_t *)calloc(1, sizeof(_cups_sp_item_t) + slen);
if (!item)
@@ -118,12 +103,9 @@ _cupsStrAlloc(const char *s) /* I - String */
item->guard = _CUPS_STR_GUARD;
DEBUG_printf(("5_cupsStrAlloc: Created string %p(%s) for \"%s\", guard=%08x, ref_count=%d", item, item->str, s, item->guard, item->ref_count));
-#endif /* DEBUG_GUARDS */
-
- /*
- * Add the string to the pool and return it...
- */
+#endif // DEBUG_GUARDS
+ // Add the string to the pool and return it...
cupsArrayAdd(stringpool, item);
cupsMutexUnlock(&sp_mutex);
@@ -132,44 +114,39 @@ _cupsStrAlloc(const char *s) /* I - String */
}
-/*
- * 'cupsConcatString()' - Safely concatenate two strings.
- */
+//
+// 'cupsConcatString()' - Safely concatenate two strings.
+//
-size_t /* O - Length of string */
-cupsConcatString(char *dst, /* O - Destination string */
- const char *src, /* I - Source string */
- size_t dstsize) /* I - Size of destination string buffer */
+size_t // O - Length of string
+cupsConcatString(char *dst, // O - Destination string
+ const char *src, // I - Source string
+ size_t dstsize) // I - Size of destination string buffer
{
+ // Range check input...
+ if (!dst || !src || dstsize == 0)
+ return (0);
+
#ifdef HAVE_STRLCAT
return (strlcat(dst, src, dstsize));
#else
- size_t srclen; /* Length of source string */
- size_t dstlen; /* Length of destination string */
-
+ size_t srclen; // Length of source string
+ size_t dstlen; // Length of destination string
- /*
- * Figure out how much room is left...
- */
+ // Figure out how much room is left...
dstlen = strlen(dst);
if (dstsize < (dstlen + 1))
- return (dstlen); /* No room, return immediately... */
+ return (dstlen); // No room, return immediately...
dstsize -= dstlen + 1;
- /*
- * Figure out how much room is needed...
- */
-
+ // Figure out how much room is needed...
srclen = strlen(src);
- /*
- * Copy the appropriate amount...
- */
-
+ // Copy the appropriate amount...
if (srclen > dstsize)
srclen = dstsize;
@@ -177,38 +154,35 @@ cupsConcatString(char *dst, /* O - Destination string */
dst[dstlen + srclen] = '\0';
return (dstlen + srclen);
-#endif /* HAVE_STRLCAT */
+#endif // HAVE_STRLCAT
}
-/*
- * 'cupsCopyString()' - Safely copy two strings.
- */
+//
+// 'cupsCopyString()' - Safely copy two strings.
+//
-size_t /* O - Length of string */
-cupsCopyString(char *dst, /* O - Destination string */
- const char *src, /* I - Source string */
- size_t dstsize) /* I - Size of destination string buffer */
+size_t // O - Length of string
+cupsCopyString(char *dst, // O - Destination string
+ const char *src, // I - Source string
+ size_t dstsize) // I - Size of destination string buffer
{
+ // Range check input...
+ if (!dst || !src || dstsize == 0)
+ return (0);
+
#ifdef HAVE_STRLCPY
return (strlcpy(dst, src, dstsize));
#else
- size_t srclen; /* Length of source string */
-
-
- /*
- * Figure out how much room is needed...
- */
+ size_t srclen; // Length of source string
+ // Figure out how much room is needed...
dstsize --;
srclen = strlen(src);
- /*
- * Copy the appropriate amount...
- */
-
+ // Copy the appropriate amount...
if (srclen > dstsize)
srclen = dstsize;
@@ -216,18 +190,18 @@ cupsCopyString(char *dst, /* O - Destination string */
dst[srclen] = '\0';
return (srclen);
-#endif /* HAVE_STRLCPY */
+#endif // HAVE_STRLCPY
}
-/*
- * '_cupsStrFlush()' - Flush the string pool.
- */
+//
+// '_cupsStrFlush()' - Flush the string pool.
+//
void
_cupsStrFlush(void)
{
- _cups_sp_item_t *item; /* Current item */
+ _cups_sp_item_t *item; // Current item
DEBUG_printf(("4_cupsStrFlush: %u strings in array", (unsigned)cupsArrayGetCount(stringpool)));
@@ -244,38 +218,31 @@ _cupsStrFlush(void)
}
-/*
- * '_cupsStrFormatd()' - Format a floating-point number.
- */
+//
+// '_cupsStrFormatd()' - Format a floating-point number.
+//
-char * /* O - Pointer to end of string */
-_cupsStrFormatd(char *buf, /* I - String */
- char *bufend, /* I - End of string buffer */
- double number, /* I - Number to format */
- struct lconv *loc) /* I - Locale data */
+char * // O - Pointer to end of string
+_cupsStrFormatd(char *buf, // I - String
+ char *bufend, // I - End of string buffer
+ double number, // I - Number to format
+ struct lconv *loc) // I - Locale data
{
- char *bufptr, /* Pointer into buffer */
- temp[1024], /* Temporary string */
- *tempdec, /* Pointer to decimal point */
- *tempptr; /* Pointer into temporary string */
- const char *dec; /* Decimal point */
- int declen; /* Length of decimal point */
-
+ char *bufptr, // Pointer into buffer
+ temp[1024], // Temporary string
+ *tempdec, // Pointer to decimal point
+ *tempptr; // Pointer into temporary string
+ const char *dec; // Decimal point
+ int declen; // Length of decimal point
- /*
- * Format the number using the "%.12f" format and then eliminate
- * unnecessary trailing 0's.
- */
+ // Format the number using the "%.12f" format and then eliminate unnecessary trailing 0's.
snprintf(temp, sizeof(temp), "%.12f", number);
- for (tempptr = temp + strlen(temp) - 1;
- tempptr > temp && *tempptr == '0';
- *tempptr-- = '\0');
-
- /*
- * Next, find the decimal point...
- */
+ tempptr = temp + strlen(temp) - 1;
+ while (tempptr > temp && *tempptr == '0')
+ *tempptr-- = '\0';
+ // Next, find the decimal point...
if (loc && loc->decimal_point)
{
dec = loc->decimal_point;
@@ -292,15 +259,15 @@ _cupsStrFormatd(char *buf, /* I - String */
else
tempdec = strstr(temp, dec);
- /*
- * Copy everything up to the decimal point...
- */
-
+ // Copy everything up to the decimal point...
if (tempdec)
{
- for (tempptr = temp, bufptr = buf;
- tempptr < tempdec && bufptr < bufend;
- *bufptr++ = *tempptr++);
+ tempptr = temp;
+ bufptr = buf;
+ while (tempptr < tempdec && bufptr < bufend)
+ {
+ *bufptr++ = *tempptr++;
+ }
tempptr += declen;
@@ -324,39 +291,33 @@ _cupsStrFormatd(char *buf, /* I - String */
}
-/*
- * '_cupsStrFree()' - Free/dereference a string.
- */
+//
+// '_cupsStrFree()' - Free/dereference a string.
+//
void
-_cupsStrFree(const char *s) /* I - String to free */
+_cupsStrFree(const char *s) // I - String to free
{
- _cups_sp_item_t *item, /* String pool item */
- *key; /* Search key */
-
+ _cups_sp_item_t *item, // String pool item
+ *key; // Search key
- /*
- * Range check input...
- */
+ // Range check input...
if (!s)
return;
- /*
- * Check the string pool...
- *
- * We don't need to lock the mutex yet, as we only want to know if
- * the stringpool is initialized. The rest of the code will still
- * work if it is initialized before we lock...
- */
+ //
+ // Check the string pool...
+ //
+ // We don't need to lock the mutex yet, as we only want to know if
+ // the stringpool is initialized. The rest of the code will still
+ // work if it is initialized before we lock...
+ //
if (!stringpool)
return;
- /*
- * See if the string is already in the pool...
- */
-
+ // See if the string is already in the pool...
cupsMutexLock(&sp_mutex);
key = (_cups_sp_item_t *)(s - offsetof(_cups_sp_item_t, str));
@@ -364,26 +325,20 @@ _cupsStrFree(const char *s) /* I - String to free */
if ((item = (_cups_sp_item_t *)cupsArrayFind(stringpool, key)) != NULL &&
item == key)
{
- /*
- * Found it, dereference...
- */
-
+ // Found it, dereference...
#ifdef DEBUG_GUARDS
if (key->guard != _CUPS_STR_GUARD)
{
DEBUG_printf(("5_cupsStrFree: Freeing string %p(%s), guard=%08x, ref_count=%d", key, key->str, key->guard, key->ref_count));
abort();
}
-#endif /* DEBUG_GUARDS */
+#endif // DEBUG_GUARDS
item->ref_count --;
if (!item->ref_count)
{
- /*
- * Remove and free...
- */
-
+ // Remove and free...
cupsArrayRemove(stringpool, item);
free(item);
@@ -394,18 +349,18 @@ _cupsStrFree(const char *s) /* I - String to free */
}
-/*
- * '_cupsStrRetain()' - Increment the reference count of a string.
- *
- * Note: This function does not verify that the passed pointer is in the
- * string pool, so any calls to it MUST know they are passing in a
- * good pointer.
- */
+//
+// '_cupsStrRetain()' - Increment the reference count of a string.
+//
+// Note: This function does not verify that the passed pointer is in the
+// string pool, so any calls to it MUST know they are passing in a
+// good pointer.
+//
-char * /* O - Pointer to string */
-_cupsStrRetain(const char *s) /* I - String to retain */
+char * // O - Pointer to string
+_cupsStrRetain(const char *s) // I - String to retain
{
- _cups_sp_item_t *item; /* Pointer to string pool item */
+ _cups_sp_item_t *item; // Pointer to string pool item
if (s)
@@ -419,7 +374,7 @@ _cupsStrRetain(const char *s) /* I - String to retain */
"ref_count=%d", item, s, item->guard, item->ref_count));
abort();
}
-#endif /* DEBUG_GUARDS */
+#endif // DEBUG_GUARDS
cupsMutexLock(&sp_mutex);
@@ -432,47 +387,41 @@ _cupsStrRetain(const char *s) /* I - String to retain */
}
-/*
- * '_cupsStrScand()' - Scan a string for a floating-point number.
- *
- * This function handles the locale-specific BS so that a decimal
- * point is always the period (".")...
- */
+//
+// '_cupsStrScand()' - Scan a string for a floating-point number.
+//
+// This function handles the locale-specific BS so that a decimal
+// point is always the period (".")...
+//
-double /* O - Number */
-_cupsStrScand(const char *buf, /* I - Pointer to number */
- char **bufptr, /* O - New pointer or NULL on error */
- struct lconv *loc) /* I - Locale data */
+double // O - Number
+_cupsStrScand(const char *buf, // I - Pointer to number
+ char **bufptr, // O - New pointer or NULL on error
+ struct lconv *loc) // I - Locale data
{
- char temp[1024], /* Temporary buffer */
- *tempptr; /* Pointer into temporary buffer */
-
+ char temp[1024], // Temporary buffer
+ *tempptr; // Pointer into temporary buffer
- /*
- * Range check input...
- */
+ // Range check input...
if (!buf)
return (0.0);
- /*
- * Skip leading whitespace...
- */
-
+ // Skip leading whitespace...
while (_cups_isspace(*buf))
buf ++;
- /*
- * Copy leading sign, numbers, period, and then numbers...
- */
-
+ // Copy leading sign, numbers, period, and then numbers...
tempptr = temp;
if (*buf == '-' || *buf == '+')
*tempptr++ = *buf++;
while (isdigit(*buf & 255))
+ {
if (tempptr < (temp + sizeof(temp) - 1))
+ {
*tempptr++ = *buf++;
+ }
else
{
if (bufptr)
@@ -480,13 +429,11 @@ _cupsStrScand(const char *buf, /* I - Pointer to number */
return (0.0);
}
+ }
if (*buf == '.')
{
- /*
- * Read fractional portion of number...
- */
-
+ // Read fractional portion of number...
buf ++;
if (loc && loc->decimal_point)
@@ -495,7 +442,9 @@ _cupsStrScand(const char *buf, /* I - Pointer to number */
tempptr += strlen(tempptr);
}
else if (tempptr < (temp + sizeof(temp) - 1))
+ {
*tempptr++ = '.';
+ }
else
{
if (bufptr)
@@ -505,8 +454,11 @@ _cupsStrScand(const char *buf, /* I - Pointer to number */
}
while (isdigit(*buf & 255))
+ {
if (tempptr < (temp + sizeof(temp) - 1))
+ {
*tempptr++ = *buf++;
+ }
else
{
if (bufptr)
@@ -514,16 +466,16 @@ _cupsStrScand(const char *buf, /* I - Pointer to number */
return (0.0);
}
+ }
}
if (*buf == 'e' || *buf == 'E')
{
- /*
- * Read exponent...
- */
-
+ // Read exponent...
if (tempptr < (temp + sizeof(temp) - 1))
+ {
*tempptr++ = *buf++;
+ }
else
{
if (bufptr)
@@ -535,7 +487,9 @@ _cupsStrScand(const char *buf, /* I - Pointer to number */
if (*buf == '+' || *buf == '-')
{
if (tempptr < (temp + sizeof(temp) - 1))
+ {
*tempptr++ = *buf++;
+ }
else
{
if (bufptr)
@@ -546,8 +500,11 @@ _cupsStrScand(const char *buf, /* I - Pointer to number */
}
while (isdigit(*buf & 255))
+ {
if (tempptr < (temp + sizeof(temp) - 1))
+ {
*tempptr++ = *buf++;
+ }
else
{
if (bufptr)
@@ -555,12 +512,10 @@ _cupsStrScand(const char *buf, /* I - Pointer to number */
return (0.0);
}
+ }
}
- /*
- * Nul-terminate the temporary string and return the value...
- */
-
+ // Nul-terminate the temporary string and return the value...
if (bufptr)
*bufptr = (char *)buf;
@@ -570,36 +525,27 @@ _cupsStrScand(const char *buf, /* I - Pointer to number */
}
-/*
- * '_cupsStrStatistics()' - Return allocation statistics for string pool.
- */
+//
+// '_cupsStrStatistics()' - Return allocation statistics for string pool.
+//
-size_t /* O - Number of strings */
-_cupsStrStatistics(size_t *alloc_bytes, /* O - Allocated bytes */
- size_t *total_bytes) /* O - Total string bytes */
+size_t // O - Number of strings
+_cupsStrStatistics(size_t *alloc_bytes, // O - Allocated bytes
+ size_t *total_bytes) // O - Total string bytes
{
- size_t count, /* Number of strings */
- abytes, /* Allocated string bytes */
- tbytes, /* Total string bytes */
- len; /* Length of string */
- _cups_sp_item_t *item; /* Current item */
+ size_t count, // Number of strings
+ abytes, // Allocated string bytes
+ tbytes, // Total string bytes
+ len; // Length of string
+ _cups_sp_item_t *item; // Current item
- /*
- * Loop through strings in pool, counting everything up...
- */
-
+ // Loop through strings in pool, counting everything up...
cupsMutexLock(&sp_mutex);
- for (count = 0, abytes = 0, tbytes = 0,
- item = (_cups_sp_item_t *)cupsArrayGetFirst(stringpool);
- item;
- item = (_cups_sp_item_t *)cupsArrayGetNext(stringpool))
+ for (count = 0, abytes = 0, tbytes = 0, item = (_cups_sp_item_t *)cupsArrayGetFirst(stringpool); item; item = (_cups_sp_item_t *)cupsArrayGetNext(stringpool))
{
- /*
- * Count allocated memory, using a 64-bit aligned buffer as a basis.
- */
-
+ // Count allocated memory, using a 64-bit aligned buffer as a basis.
count += item->ref_count;
len = (strlen(item->str) + 8) & (size_t)~7;
abytes += sizeof(_cups_sp_item_t) + len;
@@ -608,10 +554,7 @@ _cupsStrStatistics(size_t *alloc_bytes, /* O - Allocated bytes */
cupsMutexUnlock(&sp_mutex);
- /*
- * Return values...
- */
-
+ // Return values...
if (alloc_bytes)
*alloc_bytes = abytes;
@@ -622,13 +565,13 @@ _cupsStrStatistics(size_t *alloc_bytes, /* O - Allocated bytes */
}
-/*
- * '_cups_strcpy()' - Copy a string allowing for overlapping strings.
- */
+//
+// '_cups_strcpy()' - Copy a string allowing for overlapping strings.
+//
void
-_cups_strcpy(char *dst, /* I - Destination string */
- const char *src) /* I - Source string */
+_cups_strcpy(char *dst, // I - Destination string
+ const char *src) // I - Source string
{
while (*src)
*dst++ = *src++;
@@ -637,13 +580,13 @@ _cups_strcpy(char *dst, /* I - Destination string */
}
-/*
- * '_cups_strcasecmp()' - Do a case-insensitive comparison.
- */
+//
+// '_cups_strcasecmp()' - Do a case-insensitive comparison.
+//
-int /* O - Result of comparison (-1, 0, or 1) */
-_cups_strcasecmp(const char *s, /* I - First string */
- const char *t) /* I - Second string */
+int // O - Result of comparison (-1, 0, or 1)
+_cups_strcasecmp(const char *s, // I - First string
+ const char *t) // I - Second string
{
while (*s != '\0' && *t != '\0')
{
@@ -664,14 +607,14 @@ _cups_strcasecmp(const char *s, /* I - First string */
return (-1);
}
-/*
- * '_cups_strncasecmp()' - Do a case-insensitive comparison on up to N chars.
- */
+//
+// '_cups_strncasecmp()' - Do a case-insensitive comparison on up to N chars.
+//
-int /* O - Result of comparison (-1, 0, or 1) */
-_cups_strncasecmp(const char *s, /* I - First string */
- const char *t, /* I - Second string */
- size_t n) /* I - Maximum number of characters to compare */
+int // O - Result of comparison (-1, 0, or 1)
+_cups_strncasecmp(const char *s, // I - First string
+ const char *t, // I - Second string
+ size_t n) // I - Maximum number of characters to compare
{
while (*s != '\0' && *t != '\0' && n > 0)
{
@@ -696,13 +639,13 @@ _cups_strncasecmp(const char *s, /* I - First string */
}
-/*
- * 'compare_sp_items()' - Compare two string pool items...
- */
+//
+// 'compare_sp_items()' - Compare two string pool items...
+//
-static int /* O - Result of comparison */
-compare_sp_items(_cups_sp_item_t *a, /* I - First item */
- _cups_sp_item_t *b) /* I - Second item */
+static int // O - Result of comparison
+compare_sp_items(_cups_sp_item_t *a, // I - First item
+ _cups_sp_item_t *b) // I - Second item
{
return (strcmp(a->str, b->str));
}
diff --git a/cups/testcreds.c b/cups/testcreds.c
index deacbe03a..0e826987b 100644
--- a/cups/testcreds.c
+++ b/cups/testcreds.c
@@ -1,121 +1,997 @@
-/*
- * HTTP credentials test program for CUPS.
- *
- * Copyright © 2022 by OpenPrinting.
- * Copyright © 2007-2016 by Apple Inc.
- * Copyright © 1997-2006 by Easy Software Products.
- *
- * Licensed under Apache License v2.0. See the file "LICENSE" for more
- * information.
- */
-
-/*
- * Include necessary headers...
- */
+//
+// X.509 credentials test program for CUPS.
+//
+// Copyright © 2022-2023 by OpenPrinting.
+// Copyright © 2007-2016 by Apple Inc.
+// Copyright © 1997-2006 by Easy Software Products.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+// Usage: testcreds [OPTIONS] [SUB-COMMAND] [ARGUMENT]
+//
+// Sub-Commands:
+//
+// ca COMMON-NAME Sign a CSR to produce a certificate.
+// cacert COMMON-NAME Create a CA certificate.
+// cert COMMON-NAME Create a certificate.
+// client URI Connect to URI.
+// csr COMMON-NAME Create a certificate signing request.
+// server COMMON-NAME[:PORT] Run a HTTPS server (default port 8NNN.)
+// show COMMON-NAME Show stored credentials for COMMON-NAME.
+//
+// Options:
+//
+// -C COUNTRY Set country.
+// -L LOCALITY Set locality name.
+// -O ORGANIZATION Set organization name.
+// -R CSR-FILENAME Specify certificate signing request filename.
+// -S STATE Set state.
+// -U ORGANIZATIONAL-UNIT Set organizational unit name.
+// -a SUBJECT-ALT-NAME Add a subjectAltName.
+// -d DAYS Set expiration date in days.
+// -p PURPOSE Comma-delimited certificate purpose (serverAuth,
+// clientAuth, codeSigning, emailProtection,
+// timeStamping, OCSPSigning)
+// -r ROOT-NAME Name of root certificate
+// -t TYPE Certificate type (rsa-2048, rsa-3072, rsa-4096,
+// ecdsa-p256, ecdsa-p384, ecdsa-p521)
+// -u USAGE Comma-delimited key usage (digitalSignature,
+// nonRepudiation, keyEncipherment,
+// dataEncipherment, keyAgreement, keyCertSign,
+// cRLSign, encipherOnly, decipherOnly, default-ca,
+// default-tls)
+//
#include "cups-private.h"
+#include "test-internal.h"
+#include
+#include
-/*
- * 'main()' - Main entry.
- */
+//
+// Constants...
+//
-int /* O - Exit status */
-main(int argc, /* I - Number of command-line arguments */
- char *argv[]) /* I - Command-line arguments */
+#define TEST_CERT_PATH ".testssl"
+
+
+//
+// Local functions...
+//
+
+static int do_unit_tests(void);
+static int test_ca(const char *common_name, const char *csrfile, const char *root_name, int days);
+static int test_cert(bool ca_cert, cups_credpurpose_t purpose, cups_credtype_t type, cups_credusage_t keyusage, const char *organization, const char *org_unit, const char *locality, const char *state, const char *country, const char *root_name, const char *common_name, size_t num_alt_names, const char **alt_names, int days);
+static int test_client(const char *uri);
+static int test_csr(cups_credpurpose_t purpose, cups_credtype_t type, cups_credusage_t keyusage, const char *organization, const char *org_unit, const char *locality, const char *state, const char *country, const char *common_name, size_t num_alt_names, const char **alt_names);
+static int test_server(const char *host_port);
+static int test_show(const char *common_name);
+static int usage(FILE *fp);
+
+
+//
+// 'main()' - Main entry.
+//
+
+int // O - Exit status
+main(int argc, // I - Number of command-line arguments
+ char *argv[]) // I - Command-line arguments
{
- http_t *http; /* HTTP connection */
- char scheme[HTTP_MAX_URI], /* Scheme from URI */
- hostname[HTTP_MAX_URI], /* Hostname from URI */
- username[HTTP_MAX_URI], /* Username:password from URI */
- resource[HTTP_MAX_URI]; /* Resource from URI */
- int port; /* Port number from URI */
- http_trust_t trust; /* Trust evaluation for connection */
- cups_array_t *hcreds, /* Credentials from connection */
- *tcreds; /* Credentials from trust store */
- char hinfo[1024], /* String for connection credentials */
- tinfo[1024], /* String for trust store credentials */
- datestr[256]; // Date string
- static const char *trusts[] = /* Trust strings */
- { "OK", "Invalid", "Changed", "Expired", "Renewed", "Unknown" };
+ int i; // Looping var
+ const char *subcommand = NULL, // Sub-command
+ *arg = NULL, // Argument for sub-command
+ *opt, // Current option character
+ *csrfile = NULL, // Certificste signing request filename
+ *root_name = NULL, // Name of root certificate
+ *organization = NULL, // Organization
+ *org_unit = NULL, // Organizational unit
+ *locality = NULL, // Locality
+ *state = NULL, // State/province
+ *country = NULL, // Country
+ *alt_names[100]; // Subject alternate names
+ size_t num_alt_names = 0;
+ int days = 365; // Days until expiration
+ cups_credpurpose_t purpose = CUPS_CREDPURPOSE_SERVER_AUTH;
+ // Certificate purpose
+ cups_credtype_t type = CUPS_CREDTYPE_DEFAULT;
+ // Certificate type
+ cups_credusage_t keyusage = CUPS_CREDUSAGE_DEFAULT_TLS;
+ // Key usage
+
+
+ // Check command-line...
+ for (i = 1; i < argc; i ++)
+ {
+ if (!strcmp(argv[i], "--help"))
+ {
+ return (usage(stdout));
+ }
+ else if (!strncmp(argv[i], "--", 2))
+ {
+ fprintf(stderr, "testcreds: Unknown option '%s'.\n", argv[i]);
+ return (usage(stderr));
+ }
+ else if (argv[i][0] == '-')
+ {
+ for (opt = argv[i] + 1; *opt; opt ++)
+ {
+ switch (*opt)
+ {
+ case 'C' : // -C COUNTRY
+ i ++;
+ if (i >= argc)
+ {
+ fputs("testcreds: Missing country after '-C'.\n", stderr);
+ return (usage(stderr));
+ }
+ country = argv[i];
+ break;
+
+ case 'L' : // -L LOCALITY
+ i ++;
+ if (i >= argc)
+ {
+ fputs("testcreds: Missing locality/city/town after '-L'.\n", stderr);
+ return (usage(stderr));
+ }
+ locality = argv[i];
+ break;
+ case 'O' : // -O ORGANIZATION
+ i ++;
+ if (i >= argc)
+ {
+ fputs("testcreds: Missing organization after '-O'.\n", stderr);
+ return (usage(stderr));
+ }
+ organization = argv[i];
+ break;
- /*
- * Check command-line...
- */
+ case 'R' : // -R CSR-FILENAME
+ i ++;
+ if (i >= argc)
+ {
+ fputs("testcreds: Missing CSR filename after '-R'.\n", stderr);
+ return (usage(stderr));
+ }
+ csrfile = argv[i];
+ break;
- if (argc != 2)
+ case 'S' : // -S STATE
+ i ++;
+ if (i >= argc)
+ {
+ fputs("testcreds: Missing state/province after '-S'.\n", stderr);
+ return (usage(stderr));
+ }
+ state = argv[i];
+ break;
+
+ case 'U' : // -U ORGANIZATIONAL-UNIT
+ i ++;
+ if (i >= argc)
+ {
+ fputs("testcreds: Missing organizational unit after '-U'.\n", stderr);
+ return (usage(stderr));
+ }
+ org_unit = argv[i];
+ break;
+
+ case 'a' : // -a SUBJECT-ALT-NAME
+ i ++;
+ if (i >= argc)
+ {
+ fputs("testcreds: Missing subjectAltName after '-a'.\n", stderr);
+ return (usage(stderr));
+ }
+ if (num_alt_names >= (sizeof(alt_names) / sizeof(alt_names[0])))
+ {
+ fputs("testcreds: Too many subjectAltName values.\n", stderr);
+ return (1);
+ }
+ alt_names[num_alt_names ++] = argv[i];
+ break;
+
+ case 'd' : // -d DAYS
+ i ++;
+ if (i >= argc)
+ {
+ fputs("testcreds: Missing expiration days after '-d'.\n", stderr);
+ return (usage(stderr));
+ }
+ if ((days = atoi(argv[i])) <= 0)
+ {
+ fprintf(stderr, "testcreds: Bad DAYS value '%s' after '-d'.\n", argv[i]);
+ return (1);
+ }
+ break;
+
+ case 'p' : // -p PURPOSE
+ i ++;
+ if (i >= argc)
+ {
+ fputs("testcreds: Missing purpose after '-p'.\n", stderr);
+ return (usage(stderr));
+ }
+ purpose = 0;
+ if (strstr(argv[i], "serverAuth"))
+ purpose |= CUPS_CREDPURPOSE_SERVER_AUTH;
+ if (strstr(argv[i], "clientAuth"))
+ purpose |= CUPS_CREDPURPOSE_CLIENT_AUTH;
+ if (strstr(argv[i], "codeSigning"))
+ purpose |= CUPS_CREDPURPOSE_CODE_SIGNING;
+ if (strstr(argv[i], "emailProtection"))
+ purpose |= CUPS_CREDPURPOSE_EMAIL_PROTECTION;
+ if (strstr(argv[i], "timeStamping"))
+ purpose |= CUPS_CREDPURPOSE_TIME_STAMPING;
+ if (strstr(argv[i], "OCSPSigning"))
+ purpose |= CUPS_CREDPURPOSE_OCSP_SIGNING;
+ if (purpose == 0)
+ {
+ fprintf(stderr, "testcreds: Bad purpose '%s'.\n", argv[i]);
+ return (usage(stderr));
+ }
+ break;
+
+ case 'r' : // -r ROOT-NAME
+ i ++;
+ if (i >= argc)
+ {
+ fputs("testcreds: Missing root name after '-r'.\n", stderr);
+ return (usage(stderr));
+ }
+ root_name = argv[i];
+ break;
+
+ case 't' : // -t TYPE
+ i ++;
+ if (i >= argc)
+ {
+ fputs("testcreds: Missing certificate type after '-t'.\n", stderr);
+ return (usage(stderr));
+ }
+ if (!strcmp(argv[i], "default"))
+ {
+ type = CUPS_CREDTYPE_DEFAULT;
+ }
+ else if (!strcmp(argv[i], "rsa-2048"))
+ {
+ type = CUPS_CREDTYPE_RSA_2048_SHA256;
+ }
+ else if (!strcmp(argv[i], "rsa-3072"))
+ {
+ type = CUPS_CREDTYPE_RSA_3072_SHA256;
+ }
+ else if (!strcmp(argv[i], "rsa-4096"))
+ {
+ type = CUPS_CREDTYPE_RSA_4096_SHA256;
+ }
+ else if (!strcmp(argv[i], "ecdsa-p256"))
+ {
+ type = CUPS_CREDTYPE_ECDSA_P256_SHA256;
+ }
+ else if (!strcmp(argv[i], "ecdsa-p384"))
+ {
+ type = CUPS_CREDTYPE_ECDSA_P384_SHA256;
+ }
+ else if (!strcmp(argv[i], "ecdsa-p521"))
+ {
+ type = CUPS_CREDTYPE_ECDSA_P521_SHA256;
+ }
+ else
+ {
+ fprintf(stderr, "testcreds: Bad certificate type '%s'.\n", argv[i]);
+ return (usage(stderr));
+ }
+ break;
+
+ case 'u' : // -u USAGE
+ i ++;
+ if (i >= argc)
+ {
+ fputs("testcreds: Missing key usage after '-u'.\n", stderr);
+ return (usage(stderr));
+ }
+ keyusage = 0;
+ if (strstr(argv[i], "default-ca"))
+ keyusage = CUPS_CREDUSAGE_DEFAULT_CA;
+ if (strstr(argv[i], "default-tls"))
+ keyusage = CUPS_CREDUSAGE_DEFAULT_TLS;
+ if (strstr(argv[i], "digitalSignature"))
+ keyusage |= CUPS_CREDUSAGE_DIGITAL_SIGNATURE;
+ if (strstr(argv[i], "nonRepudiation"))
+ keyusage |= CUPS_CREDUSAGE_NON_REPUDIATION;
+ if (strstr(argv[i], "keyEncipherment"))
+ keyusage |= CUPS_CREDUSAGE_KEY_ENCIPHERMENT;
+ if (strstr(argv[i], "dataEncipherment"))
+ keyusage |= CUPS_CREDUSAGE_DATA_ENCIPHERMENT;
+ if (strstr(argv[i], "keyAgreement"))
+ keyusage |= CUPS_CREDUSAGE_KEY_AGREEMENT;
+ if (strstr(argv[i], "keyCertSign"))
+ keyusage |= CUPS_CREDUSAGE_KEY_CERT_SIGN;
+ if (strstr(argv[i], "cRLSign"))
+ keyusage |= CUPS_CREDUSAGE_CRL_SIGN;
+ if (strstr(argv[i], "encipherOnly"))
+ keyusage |= CUPS_CREDUSAGE_ENCIPHER_ONLY;
+ if (strstr(argv[i], "decipherOnly"))
+ keyusage |= CUPS_CREDUSAGE_DECIPHER_ONLY;
+ if (keyusage == 0)
+ {
+ fprintf(stderr, "testcreds: Bad key usage '%s'.\n", argv[i]);
+ return (usage(stderr));
+ }
+ break;
+
+ default :
+ fprintf(stderr, "testcreds: Unknown option '-%c'.\n", *opt);
+ return (usage(stderr));
+ }
+ }
+ }
+ else if (!subcommand)
+ {
+ subcommand = argv[i];
+ }
+ else if (!arg)
+ {
+ arg = argv[i];
+ }
+ else
+ {
+ fprintf(stderr, "testcreds: Unknown option '%s'.\n", argv[i]);
+ return (usage(stderr));
+ }
+ }
+
+ // Make certificate directory
+ if (access(TEST_CERT_PATH, 0))
+ mkdir(TEST_CERT_PATH, 0700);
+
+ // Do unit tests or sub-command...
+ if (!subcommand)
{
- puts("Usage: ./testcreds hostname");
- puts(" ./testcreds https://hostname[:port]");
- return (1);
+ return (do_unit_tests());
+ }
+ else if (!arg)
+ {
+ fputs("testcreds: Missing sub-command argument.\n", stderr);
+ return (usage(stderr));
+ }
+
+ // Run the corresponding sub-command...
+ if (!strcmp(subcommand, "ca"))
+ {
+ return (test_ca(arg, csrfile, root_name, days));
+ }
+ else if (!strcmp(subcommand, "cacert"))
+ {
+ return (test_cert(true, purpose, type, keyusage, organization, org_unit, locality, state, country, root_name, arg, num_alt_names, alt_names, days));
+ }
+ else if (!strcmp(subcommand, "cert"))
+ {
+ return (test_cert(false, purpose, type, keyusage, organization, org_unit, locality, state, country, root_name, arg, num_alt_names, alt_names, days));
+ }
+ else if (!strcmp(subcommand, "client"))
+ {
+ return (test_client(arg));
+ }
+ else if (!strcmp(subcommand, "csr"))
+ {
+ return (test_csr(purpose, type, keyusage, organization, org_unit, locality, state, country, arg, num_alt_names, alt_names));
+ }
+ else if (!strcmp(subcommand, "server"))
+ {
+ return (test_server(arg));
+ }
+ else if (!strcmp(subcommand, "show"))
+ {
+ return (test_show(arg));
+ }
+ else
+ {
+ fprintf(stderr, "testcreds: Unknown sub-command '%s'.\n", subcommand);
+ return (usage(stderr));
+ }
+}
+
+
+//
+// 'do_unit_tests()' - Do unit tests.
+//
+
+static int // O - Exit status
+do_unit_tests(void)
+{
+ cups_credtype_t type; // Current credential type
+ char *data; // Cert data
+ static const char * const alt_names[] =
+ { // subjectAltName values
+ "printer.example.com",
+ "localhost"
+ };
+ static const char * const types[] =
+ { // Credential types
+ "default",
+ "rsa-2048",
+ "rsa-3072",
+ "rsa-4096",
+ "ecdsa-p256",
+ "ecdsa-p384",
+ "ecdsa-p521"
+ };
+
+
+ for (type = CUPS_CREDTYPE_DEFAULT; type <= CUPS_CREDTYPE_ECDSA_P521_SHA256; type ++)
+ {
+ testBegin("cupsCreateCredentials(_site_, %s, CA)", types[type]);
+ if (cupsCreateCredentials(TEST_CERT_PATH, true, CUPS_CREDPURPOSE_SERVER_AUTH, type, CUPS_CREDUSAGE_DEFAULT_TLS, "Organization", "Unit", "Locality", "Ontario", "CA", "_site_", 0, NULL, NULL, time(NULL) + 30 * 86400))
+ {
+ testEnd(true);
+
+ testBegin("cupsCopyCredentials(_site_)");
+ data = cupsCopyCredentials(TEST_CERT_PATH, "_site_");
+ testEnd(data != NULL);
+ free(data);
+
+ testBegin("cupsCopyCredentialsKey(_site_)");
+ data = cupsCopyCredentialsKey(TEST_CERT_PATH, "_site_");
+ testEnd(data != NULL);
+ free(data);
+ }
+ else
+ {
+ testEndMessage(false, "%s", cupsLastErrorString());
+ }
+
+ testBegin("cupsCreateCredentials(printer w/alt names, %s, signed by CA cert)", types[type]);
+ if (cupsCreateCredentials(TEST_CERT_PATH, false, CUPS_CREDPURPOSE_SERVER_AUTH, type, CUPS_CREDUSAGE_DEFAULT_TLS, "Organization", "Unit", "Locality", "Ontario", "CA", "printer", sizeof(alt_names) / sizeof(alt_names[0]), alt_names, "_site_", time(NULL) + 30 * 86400))
+ testEnd(true);
+ else
+ testEndMessage(false, "%s", cupsLastErrorString());
+
+ testBegin("cupsCreateCredentialsRequest(altprinter w/alt names, %s)", types[type]);
+ if (cupsCreateCredentialsRequest(TEST_CERT_PATH, CUPS_CREDPURPOSE_SERVER_AUTH, type, CUPS_CREDUSAGE_DEFAULT_TLS, "Organization", "Unit", "Locality", "Ontario", "CA", "altprinter", sizeof(alt_names) / sizeof(alt_names[0]), alt_names))
+ {
+ testEnd(true);
+
+ testBegin("cupsCopyCredentialsKey(altprinter w/alt names)");
+ data = cupsCopyCredentialsKey(TEST_CERT_PATH, "altprinter");
+ testEnd(data != NULL);
+ free(data);
+
+ testBegin("cupsCopyCredentialsRequest(altprinter w/alt names)");
+ data = cupsCopyCredentialsRequest(TEST_CERT_PATH, "altprinter");
+ testEnd(data != NULL);
+
+ if (data)
+ {
+ testBegin("cupsSignCredentialsRequest(altprinter w/alt names)");
+ if (cupsSignCredentialsRequest(TEST_CERT_PATH, "altprinter", data, "_site_", CUPS_CREDPURPOSE_ALL, CUPS_CREDUSAGE_ALL, /*cb*/NULL, /*cb_data*/NULL, time(NULL) + 30 * 86400))
+ {
+ testEndMessage(false, "Expected a failure");
+ }
+ else
+ {
+ testEndMessage(true, "%s", cupsLastErrorString());
+ }
+
+ free(data);
+ }
+ }
+ else
+ {
+ testEndMessage(false, "%s", cupsLastErrorString());
+ }
+
+ testBegin("cupsCreateCredentialsRequest(altprinter w/o alt names, %s)", types[type]);
+ if (cupsCreateCredentialsRequest(TEST_CERT_PATH, CUPS_CREDPURPOSE_SERVER_AUTH, type, CUPS_CREDUSAGE_DEFAULT_TLS, "Organization", "Unit", "Locality", "Ontario", "CA", "altprinter", 0, NULL))
+ {
+ testEnd(true);
+
+ testBegin("cupsCopyCredentialsKey(altprinter w/o alt names)");
+ data = cupsCopyCredentialsKey(TEST_CERT_PATH, "altprinter");
+ testEnd(data != NULL);
+ free(data);
+
+ testBegin("cupsCopyCredentialsRequest(altprinter w/o alt names)");
+ data = cupsCopyCredentialsRequest(TEST_CERT_PATH, "altprinter");
+ testEnd(data != NULL);
+
+ if (data)
+ {
+ testBegin("cupsSignCredentialsRequest(altprinter w/o alt names)");
+ if (cupsSignCredentialsRequest(TEST_CERT_PATH, "altprinter", data, "_site_", CUPS_CREDPURPOSE_ALL, CUPS_CREDUSAGE_ALL, /*cb*/NULL, /*cb_data*/NULL, time(NULL) + 30 * 86400))
+ {
+ testEnd(true);
+ free(data);
+
+ testBegin("cupsCopyCredentialsKey(altprinter w/o alt names)");
+ data = cupsCopyCredentialsKey(TEST_CERT_PATH, "altprinter");
+ testEnd(data != NULL);
+ }
+ else
+ {
+ testEndMessage(false, "%s", cupsLastErrorString());
+ }
+
+ free(data);
+ }
+ }
+ else
+ {
+ testEndMessage(false, "%s", cupsLastErrorString());
+ }
}
- if (!strncmp(argv[1], "https://", 8))
+ return (testsPassed ? 0 : 1);
+}
+
+
+//
+// 'test_ca()' - Test generating a certificate from a CSR.
+//
+
+static int // O - Exit status
+test_ca(const char *common_name, // I - Common name
+ const char *csrfile, // I - CSR filename, if any
+ const char *root_name, // I - Root certificate name
+ int days) // I - Number of days
+{
+ char *request, // Certificate request
+ *cert; // Certificate
+
+
+ if (csrfile)
{
- /*
- * Connect to the host and validate credentials...
- */
+ int csrfd = open(csrfile, O_RDONLY);
+ // File descriptor
+ struct stat csrinfo; // File information
- if (httpSeparateURI(HTTP_URI_CODING_MOST, argv[1], scheme, sizeof(scheme), username, sizeof(username), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
+ if (csrfd < 0)
{
- printf("ERROR: Bad URI \"%s\".\n", argv[1]);
+ fprintf(stderr, "testcreds: Unable to access '%s': %s\n", csrfile, strerror(errno));
return (1);
}
- if ((http = httpConnect(hostname, port, NULL, AF_UNSPEC, HTTP_ENCRYPTION_ALWAYS, 1, 30000, NULL)) == NULL)
+ if (fstat(csrfd, &csrinfo))
{
- printf("ERROR: Unable to connect to \"%s\" on port %d: %s\n", hostname, port, cupsLastErrorString());
+ fprintf(stderr, "testcreds: Unable to stat '%s': %s\n", csrfile, strerror(errno));
+ close(csrfd);
return (1);
}
- puts("HTTP Credentials:");
- if (httpCopyCredentials(http, &hcreds))
+ if ((request = malloc((size_t)csrinfo.st_size + 1)) == NULL)
{
- trust = httpCredentialsGetTrust(hcreds, hostname);
+ fprintf(stderr, "testcreds: Unable to allocate memory for '%s': %s\n", csrfile, strerror(errno));
+ close(csrfd);
+ return (1);
+ }
- httpCredentialsString(hcreds, hinfo, sizeof(hinfo));
+ if (read(csrfd, request, (size_t)csrinfo.st_size) < (ssize_t)csrinfo.st_size)
+ {
+ fprintf(stderr, "testcreds: Unable to read '%s'.\n", csrfile);
+ close(csrfd);
+ return (1);
+ }
- printf(" Certificate Count: %u\n", (unsigned)cupsArrayGetCount(hcreds));
- if (trust == HTTP_TRUST_OK)
- puts(" Trust: OK");
- else
- printf(" Trust: %s (%s)\n", trusts[trust], cupsLastErrorString());
- printf(" Expiration: %s\n", httpGetDateString(httpCredentialsGetExpiration(hcreds), datestr, sizeof(datestr)));
- printf(" IsValidName: %d\n", httpCredentialsAreValidForName(hcreds, hostname));
- printf(" String: \"%s\"\n", hinfo);
+ close(csrfd);
+ request[csrinfo.st_size] = '\0';
+ }
+ else if ((request = cupsCopyCredentialsRequest(TEST_CERT_PATH, common_name)) == NULL)
+ {
+ fprintf(stderr, "testcreds: No request for '%s'.\n", common_name);
+ return (1);
+ }
- httpFreeCredentials(hcreds);
- }
+ if (!cupsSignCredentialsRequest(TEST_CERT_PATH, common_name, request, root_name, CUPS_CREDPURPOSE_ALL, CUPS_CREDUSAGE_ALL, /*cb*/NULL, /*cb_data*/NULL, time(NULL) + days * 86400))
+ {
+ fprintf(stderr, "testcreds: Unable to create certificate (%s)\n", cupsLastErrorString());
+ free(request);
+ return (1);
+ }
+
+ free(request);
+
+ if ((cert = cupsCopyCredentials(TEST_CERT_PATH, common_name)) != NULL)
+ {
+ puts(cert);
+ free(cert);
+ }
+ else
+ {
+ fprintf(stderr, "testcreds: Unable to get generated certificate for '%s'.\n", common_name);
+ return (1);
+ }
+
+ return (0);
+}
+
+
+//
+// 'test_cert()' - Test creating a self-signed certificate.
+//
+
+static int // O - Exit status
+test_cert(
+ bool ca_cert, // I - `true` for a CA certificate, `false` for a regular one
+ cups_credpurpose_t purpose, // I - Certificate purpose
+ cups_credtype_t type, // I - Certificate type
+ cups_credusage_t keyusage, // I - Key usage
+ const char *organization, // I - Organization
+ const char *org_unit, // I - Organizational unit
+ const char *locality, // I - Locality (city/town/etc.)
+ const char *state, // I - State/province
+ const char *country, // I - Country
+ const char *root_name, // I - Root certificate name
+ const char *common_name, // I - Common name
+ size_t num_alt_names, // I - Number of subjectAltName's
+ const char **alt_names, // I - subjectAltName's
+ int days) // I - Number of days until expiration
+{
+ char *cert, // Certificate
+ *key; // Private key
+
+
+ if (!cupsCreateCredentials(TEST_CERT_PATH, ca_cert, purpose, type, keyusage, organization, org_unit, locality, state, country, common_name, num_alt_names, alt_names, root_name, time(NULL) + days * 86400))
+ {
+ fprintf(stderr, "testcreds: Unable to create certificate (%s)\n", cupsLastErrorString());
+ return (1);
+ }
+
+ if ((cert = cupsCopyCredentials(TEST_CERT_PATH, common_name)) != NULL)
+ {
+ puts(cert);
+ free(cert);
+ }
+ else
+ {
+ fprintf(stderr, "testcreds: Unable to get generated certificate for '%s'.\n", common_name);
+ return (1);
+ }
+
+ if ((key = cupsCopyCredentialsKey(TEST_CERT_PATH, common_name)) != NULL)
+ {
+ puts(key);
+ free(key);
+ }
+ else
+ {
+ fprintf(stderr, "testcreds: Unable to get generated private key for '%s'.\n", common_name);
+ return (1);
+ }
+
+ return (0);
+}
+
+
+//
+// 'test_client()' - Test connecting to a HTTPS server.
+//
+
+static int // O - Exit status
+test_client(const char *uri) // I - URI
+{
+ http_t *http; // HTTP connection
+ char scheme[HTTP_MAX_URI], // Scheme from URI
+ hostname[HTTP_MAX_URI], // Hostname from URI
+ username[HTTP_MAX_URI], // Username:password from URI
+ resource[HTTP_MAX_URI]; // Resource from URI
+ int port; // Port number from URI
+ http_trust_t trust; // Trust evaluation for connection
+ cups_array_t *hcreds; // Credentials from connection
+ char hinfo[1024], // String for connection credentials
+ datestr[256]; // Date string
+ static const char *trusts[] = // Trust strings
+ { "OK", "Invalid", "Changed", "Expired", "Renewed", "Unknown" };
+
+
+ // Connect to the host and validate credentials...
+ if (httpSeparateURI(HTTP_URI_CODING_MOST, uri, scheme, sizeof(scheme), username, sizeof(username), hostname, sizeof(hostname), &port, resource, sizeof(resource)) < HTTP_URI_STATUS_OK)
+ {
+ fprintf(stderr, "testcreds: Bad URI '%s'.\n", uri);
+ return (1);
+ }
+
+ if ((http = httpConnect(hostname, port, NULL, AF_UNSPEC, HTTP_ENCRYPTION_ALWAYS, 1, 30000, NULL)) == NULL)
+ {
+ fprintf(stderr, "testcreds: Unable to connect to '%s' on port %d: %s\n", hostname, port, cupsLastErrorString());
+ return (1);
+ }
+
+ puts("TLS Server Credentials:");
+ if (httpCopyCredentials(http, &hcreds))
+ {
+ trust = httpCredentialsGetTrust(hcreds, hostname);
+
+ httpCredentialsString(hcreds, hinfo, sizeof(hinfo));
+
+ printf(" Certificate Count: %u\n", (unsigned)cupsArrayGetCount(hcreds));
+ if (trust == HTTP_TRUST_OK)
+ puts(" Trust: OK");
else
- puts(" Not present (error).");
+ printf(" Trust: %s (%s)\n", trusts[trust], cupsLastErrorString());
+ printf(" Expiration: %s\n", httpGetDateString(httpCredentialsGetExpiration(hcreds), datestr, sizeof(datestr)));
+ printf(" IsValidName: %s\n", httpCredentialsAreValidForName(hcreds, hostname) ? "true" : "false");
+ printf(" String: \"%s\"\n", hinfo);
+
+ httpFreeCredentials(hcreds);
+ }
+ else
+ {
+ puts(" Not present (error).");
+ }
+
+ puts("");
+
+ return (test_show(hostname));
+}
+
+
+//
+// 'test_csr()' - Test creating a certificate signing request.
+//
+
+static int // O - Exit status
+test_csr(
+ cups_credpurpose_t purpose, // I - Certificate purpose
+ cups_credtype_t type, // I - Certificate type
+ cups_credusage_t keyusage, // I - Key usage
+ const char *organization, // I - Organization
+ const char *org_unit, // I - Organizational unit
+ const char *locality, // I - Locality (city/town/etc.)
+ const char *state, // I - State/province
+ const char *country, // I - Country
+ const char *common_name, // I - Common name
+ size_t num_alt_names, // I - Number of subjectAltName's
+ const char **alt_names) // I - subjectAltName's
+{
+ char *csr; // Certificate request
+
+
+ if (!cupsCreateCredentialsRequest(TEST_CERT_PATH, purpose, type, keyusage, organization, org_unit, locality, state, country, common_name, num_alt_names, alt_names))
+ {
+ fprintf(stderr, "testcreds: Unable to create certificate request (%s)\n", cupsLastErrorString());
+ return (1);
+ }
+
+ if ((csr = cupsCopyCredentialsRequest(TEST_CERT_PATH, common_name)) != NULL)
+ {
+ puts(csr);
+ free(csr);
+ }
+ else
+ {
+ fprintf(stderr, "testcreds: Unable to get generated certificate request for '%s'.\n", common_name);
+ return (1);
+ }
+
+ return (0);
+}
+
- puts("");
+//
+// 'test_server()' - Test running a server.
+//
+
+static int // O - Exit status
+test_server(const char *host_port) // I - Hostname/port
+{
+ char host[256], // Hostname
+ *hostptr; // Pointer into hostname
+ int port; // Port number
+ nfds_t i, // Looping var
+ num_listeners = 0; // Number of listeners
+ struct pollfd listeners[2]; // Listeners
+ http_addr_t addr; // Listen address
+ http_t *http; // Client
+
+
+ // Get the host and port...
+ cupsCopyString(host, host_port, sizeof(host));
+ if ((hostptr = strrchr(host, ':')) != NULL)
+ {
+ // Extract the port number from the argument...
+ *hostptr++ = '\0';
+ port = atoi(hostptr);
}
else
{
- /*
- * Load stored credentials...
- */
+ // Use the default port 8NNN where NNN is the bottom 3 digits of the UID...
+ port = 8000 + (int)getuid() % 1000;
+ }
+
+ // Setup listeners for IPv4 and IPv6...
+ memset(&addr, 0, sizeof(addr));
+ addr.ipv4.sin_family = AF_INET;
- cupsCopyString(hostname, argv[1], sizeof(hostname));
+ if ((listeners[num_listeners].fd = httpAddrListen(&addr, port)) > 0)
+ {
+ listeners[num_listeners].events = POLLIN | POLLERR;
+ num_listeners ++;
}
- printf("Trust Store for \"%s\":\n", hostname);
+ addr.ipv6.sin6_family = AF_INET6;
+
+ if ((listeners[num_listeners].fd = httpAddrListen(&addr, port)) > 0)
+ {
+ listeners[num_listeners].events = POLLIN | POLLERR;
+ num_listeners ++;
+ }
+
+ if (num_listeners == 0)
+ {
+ fprintf(stderr, "testcreds: Unable to listen on port %d: %s\n", port, cupsLastErrorString());
+ return (1);
+ }
+
+ printf("Listening for connections on port %d...\n", port);
+
+ // Set certificate info...
+ cupsSetServerCredentials(TEST_CERT_PATH, host, true);
- if (httpLoadCredentials(NULL, &tcreds, hostname))
+ // Wait for connections...
+ for (;;)
+ {
+ http_state_t state; // HTTP request state
+ char resource[1024]; // Resource path
+
+ // Look for new connections...
+ if (poll(listeners, num_listeners, 1000) < 0)
+ {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+
+ perror("testcreds: Unable to poll");
+ break;
+ }
+
+ // Try accepting a connection...
+ for (i = 0, http = NULL; i < num_listeners; i ++)
+ {
+ if (listeners[i].revents & POLLIN)
+ {
+ if ((http = httpAcceptConnection(listeners[i].fd, true)) != NULL)
+ break;
+
+ fprintf(stderr, "testcreds: Unable to accept connection: %s\n", cupsLastErrorString());
+ }
+ }
+
+ if (!http)
+ continue;
+
+ // Negotiate a secure connection...
+ if (!httpSetEncryption(http, HTTP_ENCRYPTION_ALWAYS))
+ {
+ fprintf(stderr, "testcreds: Unable to encrypt connection: %s\n", cupsLastErrorString());
+ httpClose(http);
+ continue;
+ }
+
+ // Process a single request and then close it out...
+ while ((state = httpReadRequest(http, resource, sizeof(resource))) == HTTP_STATE_WAITING)
+ usleep(1000);
+
+ if (state == HTTP_STATE_ERROR)
+ {
+ if (httpError(http) == EPIPE)
+ fputs("testcreds: Client closed connection.\n", stderr);
+ else
+ fprintf(stderr, "testcreds: Bad request line (%s).\n", strerror(httpError(http)));
+ }
+ else if (state == HTTP_STATE_UNKNOWN_METHOD)
+ {
+ fputs("testcreds: Bad/unknown operation.\n", stderr);
+ }
+ else if (state == HTTP_STATE_UNKNOWN_VERSION)
+ {
+ fputs("testcreds: Bad HTTP version.\n", stderr);
+ }
+ else
+ {
+ printf("%s %s\n", httpStateString(state), resource);
+
+ if (state == HTTP_STATE_GET || state == HTTP_STATE_HEAD)
+ {
+ httpClearFields(http);
+ httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "text/plain");
+ httpSetField(http, HTTP_FIELD_CONNECTION, "close");
+ httpSetLength(http, strlen(resource) + 1);
+ httpWriteResponse(http, HTTP_STATUS_OK);
+
+ if (state == HTTP_STATE_GET)
+ {
+ // Echo back the resource path...
+ httpWrite(http, resource, strlen(resource));
+ httpWrite(http, "\n", 1);
+ httpFlushWrite(http);
+ }
+ }
+ else
+ {
+ httpWriteResponse(http, HTTP_STATUS_BAD_REQUEST);
+ }
+ }
+
+ httpClose(http);
+ }
+
+ // Close listeners and return...
+ for (i = 0; i < num_listeners; i ++)
+ httpAddrClose(&addr, listeners[i].fd);
+
+ return (0);
+}
+
+
+//
+// 'test_show()' - Test showing stored certificates.
+//
+
+static int // O - Exit status
+test_show(const char *common_name) // I - Common name
+{
+ cups_array_t *tcreds; // Credentials from trust store
+ char tinfo[1024], // String for trust store credentials
+ datestr[256]; // Date string
+
+
+ printf("Trust Store for \"%s\":\n", common_name);
+
+ if (httpLoadCredentials(NULL, &tcreds, common_name))
{
httpCredentialsString(tcreds, tinfo, sizeof(tinfo));
printf(" Certificate Count: %u\n", (unsigned)cupsArrayGetCount(tcreds));
printf(" Expiration: %s\n", httpGetDateString(httpCredentialsGetExpiration(tcreds), datestr, sizeof(datestr)));
- printf(" IsValidName: %d\n", httpCredentialsAreValidForName(tcreds, hostname));
+ printf(" IsValidName: %s\n", httpCredentialsAreValidForName(tcreds, common_name) ? "true" : "false");
printf(" String: \"%s\"\n", tinfo);
httpFreeCredentials(tcreds);
}
else
+ {
puts(" Not present.");
+ }
return (0);
}
+
+
+//
+// 'usage()' - Show program usage...
+//
+
+static int // O - Exit code
+usage(FILE *fp) // I - Output file (stdout or stderr)
+{
+ fputs("Usage: testcreds [OPTIONS] [SUB-COMMAND] [ARGUMENT]\n", fp);
+ fputs("\n", fp);
+ fputs("Sub-Commands:\n", fp);
+ fputs("\n", fp);
+ fputs(" ca COMMON-NAME Sign a CSR to produce a certificate.\n", fp);
+ fputs(" cacert COMMON-NAME Create a CA certificate.\n", fp);
+ fputs(" cert COMMON-NAME Create a certificate.\n", fp);
+ fputs(" client URI Connect to URI.\n", fp);
+ fputs(" csr COMMON-NAME Create a certificate signing request.\n", fp);
+ fputs(" server COMMON-NAME[:PORT] Run a HTTPS server (default port 8NNN.)\n", fp);
+ fputs(" show COMMON-NAME Show stored credentials for COMMON-NAME.\n", fp);
+ fputs("\n", fp);
+ fputs("Options:\n", fp);
+ fputs("\n", fp);
+ fputs(" -C COUNTRY Set country.\n", fp);
+ fputs(" -L LOCALITY Set locality name.\n", fp);
+ fputs(" -O ORGANIZATION Set organization name.\n", fp);
+ fputs(" -R CSR-FILENAME Specify certificate signing request file.\n", fp);
+ fputs(" -S STATE Set state.\n", fp);
+ fputs(" -U ORGANIZATIONAL-UNIT Set organizational unit name.\n", fp);
+ fputs(" -a SUBJECT-ALT-NAME Add a subjectAltName.\n", fp);
+ fputs(" -d DAYS Set expiration date in days.\n", fp);
+ fputs(" -p PURPOSE Comma-delimited certificate purpose (serverAuth, clientAuth, codeSigning, emailProtection, timeStamping, OCSPSigning)\n", fp);
+ fputs(" -r ROOT-NAME Name of root certificate\n", fp);
+ fputs(" -t TYPE Certificate type (rsa-2048, rsa-3072, rsa-4096, ecdsa-p256, ecdsa-p384, ecdsa-p521)\n", fp);
+ fputs(" -u USAGE Comma-delimited key usage (digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign, encipherOnly, decipherOnly, default-ca, default-tls)\n", fp);
+
+ return (fp == stderr);
+}
diff --git a/cups/testhttp.c b/cups/testhttp.c
index 60afda9ba..161062683 100644
--- a/cups/testhttp.c
+++ b/cups/testhttp.c
@@ -675,7 +675,7 @@ main(int argc, /* I - Number of command-line arguments */
printf("Count: %u\n", (unsigned)cupsArrayGetCount(creds));
printf("Trust: %s\n", trusts[trust]);
printf("Expiration: %s\n", httpGetDateString(httpCredentialsGetExpiration(creds), expstr, sizeof(expstr)));
- printf("IsValidName: %d\n", httpCredentialsAreValidForName(creds, hostname));
+ printf("IsValidName: %s\n", httpCredentialsAreValidForName(creds, hostname) ? "true" : "false");
printf("String: \"%s\"\n", info);
printf("LoadCredentials: %s\n", httpLoadCredentials(NULL, &lcreds, hostname) ? "true" : "false");
diff --git a/cups/testpwg.c b/cups/testpwg.c
index 6473c8cf9..feacdb1e0 100644
--- a/cups/testpwg.c
+++ b/cups/testpwg.c
@@ -1,565 +1,167 @@
-/*
- * PWG unit test program for CUPS.
- *
- * Copyright 2009-2016 by Apple Inc.
- *
- * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
- */
+//
+// PWG unit test program for CUPS.
+//
+// Copyright © 2023 by OpenPrinting.
+// Copyright © 2009-2016 by Apple Inc.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more information.
+//
/*
* Include necessary headers...
*/
-#include "ppd-private.h"
-#include "file-private.h"
-
-
-/*
- * Local functions...
- */
+#include "cups.h"
+#include "pwg-private.h"
+#include "test-internal.h"
+#include
-static int test_pagesize(_ppd_cache_t *pc, ppd_file_t *ppd,
- const char *ppdsize);
-static int test_ppd_cache(_ppd_cache_t *pc, ppd_file_t *ppd);
+//
+// 'main()' - Main entry.
+//
-/*
- * 'main()' - Main entry.
- */
-
-int /* O - Exit status */
-main(int argc, /* I - Number of command-line args */
- char *argv[]) /* I - Command-line arguments */
+int // O - Exit status
+main(int argc, // I - Number of command-line args
+ char *argv[]) // I - Command-line arguments
{
- int status; /* Status of tests (0 = success, 1 = fail) */
- const char *ppdfile; /* PPD filename */
- ppd_file_t *ppd; /* PPD file */
- _ppd_cache_t *pc; /* PPD cache and PWG mapping data */
- const pwg_media_t *pwgmedia; /* PWG media size */
- size_t i, /* Looping var */
- num_media; /* Number of media sizes */
- const pwg_media_t *mediatable; /* Media size table */
- int dupmedia = 0; /* Duplicate media sizes? */
-
-
- status = 0;
+ const pwg_media_t *pwg; // PWG media size
+ size_t i, // Looping var
+ num_media; // Number of media sizes
+ const pwg_media_t *mediatable; // Media size table
- if (argc < 2 || argc > 3)
- {
- puts("Usage: ./testpwg filename.ppd [jobfile]");
- return (1);
- }
-
- ppdfile = argv[1];
-
- printf("ppdOpenFile(%s): ", ppdfile);
- if ((ppd = ppdOpenFile(ppdfile)) == NULL)
- {
- ppd_status_t err; /* Last error in file */
- int line; /* Line number in file */
-
-
- err = ppdLastError(&line);
-
- printf("FAIL (%s on line %d)\n", ppdErrorString(err), line);
-
- return (1);
- }
- else
- puts("PASS");
- fputs("_ppdCacheCreateWithPPD(ppd): ", stdout);
- if ((pc = _ppdCacheCreateWithPPD(ppd)) == NULL)
- {
- puts("FAIL");
- status ++;
- }
- else
+ if (argc > 1)
{
- puts("PASS");
- status += test_ppd_cache(pc, ppd);
+ // Decode media sizes...
+ int j; // Looping var
- if (argc == 3)
+ for (j = 1; j < argc; j ++)
{
- /*
- * Test PageSize mapping code.
- */
-
- int fd; /* Job file descriptor */
- const char *pagesize; /* PageSize value */
- ipp_t *job; /* Job attributes */
- ipp_attribute_t *media; /* Media attribute */
+ int width, length; // Width and length
- if ((fd = open(argv[2], O_RDONLY)) >= 0)
- {
- job = ippNew();
- ippReadFile(fd, job);
- close(fd);
+ if (isdigit(argv[j][0] & 255) && sscanf(argv[j], "%d,%d", &width, &length) == 2)
+ pwg = pwgMediaForSize(width, length);
+ else if ((pwg = pwgMediaForLegacy(argv[j])) == NULL)
+ pwg = pwgMediaForPWG(argv[j]);
- if ((media = ippFindAttribute(job, "media", IPP_TAG_ZERO)) != NULL &&
- media->value_tag != IPP_TAG_NAME &&
- media->value_tag != IPP_TAG_KEYWORD)
- media = NULL;
-
- if (media)
- printf("_ppdCacheGetPageSize(media=%s): ",
- media->values[0].string.text);
- else
- fputs("_ppdCacheGetPageSize(media-col): ", stdout);
-
- fflush(stdout);
-
- if ((pagesize = _ppdCacheGetPageSize(pc, job, NULL, NULL)) == NULL)
- {
- puts("FAIL (Not Found)");
- status = 1;
- }
- else if (media && _cups_strcasecmp(pagesize, media->values[0].string.text))
- {
- printf("FAIL (Got \"%s\", Expected \"%s\")\n", pagesize,
- media->values[0].string.text);
- status = 1;
- }
- else
- printf("PASS (%s)\n", pagesize);
-
- ippDelete(job);
- }
+ if (pwg)
+ printf("%s = %s (%.2fx%.2fmm", argv[j], pwg->pwg, pwg->width * 0.01, pwg->length * 0.01);
else
- {
- perror(argv[2]);
- status = 1;
- }
+ printf("%s = BAD\n", argv[j]);
}
- /*
- * _ppdCacheDestroy should never fail...
- */
-
- fputs("_ppdCacheDestroy(pc): ", stdout);
- _ppdCacheDestroy(pc);
- puts("PASS");
+ return (0);
}
- fputs("pwgMediaForPWG(\"iso_a4_210x297mm\"): ", stdout);
- if ((pwgmedia = pwgMediaForPWG("iso_a4_210x297mm")) == NULL)
- {
- puts("FAIL (not found)");
- status ++;
- }
- else if (strcmp(pwgmedia->pwg, "iso_a4_210x297mm"))
- {
- printf("FAIL (%s)\n", pwgmedia->pwg);
- status ++;
- }
- else if (pwgmedia->width != 21000 || pwgmedia->length != 29700)
- {
- printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
- status ++;
- }
+ testBegin("pwgMediaForPWG(\"iso_a4_210x297mm\")");
+ if ((pwg = pwgMediaForPWG("iso_a4_210x297mm")) == NULL)
+ testEndMessage(false, "not found");
+ else if (strcmp(pwg->pwg, "iso_a4_210x297mm"))
+ testEndMessage(false, "%s", pwg->pwg);
+ else if (pwg->width != 21000 || pwg->length != 29700)
+ testEndMessage(false, "%dx%d", pwg->width, pwg->length);
else
- puts("PASS");
+ testEnd(true);
- fputs("pwgMediaForPWG(\"roll_max_36.1025x3622.0472in\"): ", stdout);
- if ((pwgmedia = pwgMediaForPWG("roll_max_36.1025x3622.0472in")) == NULL)
- {
- puts("FAIL (not found)");
- status ++;
- }
- else if (pwgmedia->width != 91700 || pwgmedia->length != 9199999)
- {
- printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
- status ++;
- }
+ testBegin("pwgMediaForPWG(\"roll_max_36.1025x3622.0472in\")");
+ if ((pwg = pwgMediaForPWG("roll_max_36.1025x3622.0472in")) == NULL)
+ testEndMessage(false, "not found");
+ else if (pwg->width != 91700 || pwg->length != 9199999)
+ testEndMessage(false, "%dx%d", pwg->width, pwg->length);
else
- printf("PASS (%dx%d)\n", pwgmedia->width, pwgmedia->length);
+ testEndMessage(true, "%dx%d", pwg->width, pwg->length);
- fputs("pwgMediaForPWG(\"disc_test_10x100mm\"): ", stdout);
- if ((pwgmedia = pwgMediaForPWG("disc_test_10x100mm")) == NULL)
- {
- puts("FAIL (not found)");
- status ++;
- }
- else if (pwgmedia->width != 10000 || pwgmedia->length != 10000)
- {
- printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
- status ++;
- }
+ testBegin("pwgMediaForPWG(\"disc_test_10x100mm\")");
+ if ((pwg = pwgMediaForPWG("disc_test_10x100mm")) == NULL)
+ testEndMessage(false, "not found");
+ else if (pwg->width != 10000 || pwg->length != 10000)
+ testEndMessage(false, "%dx%d", pwg->width, pwg->length);
else
- printf("PASS (%dx%d)\n", pwgmedia->width, pwgmedia->length);
-
- fputs("pwgMediaForLegacy(\"na-letter\"): ", stdout);
- if ((pwgmedia = pwgMediaForLegacy("na-letter")) == NULL)
- {
- puts("FAIL (not found)");
- status ++;
- }
- else if (strcmp(pwgmedia->pwg, "na_letter_8.5x11in"))
- {
- printf("FAIL (%s)\n", pwgmedia->pwg);
- status ++;
- }
- else if (pwgmedia->width != 21590 || pwgmedia->length != 27940)
- {
- printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
- status ++;
- }
+ testEndMessage(true, "%dx%d", pwg->width, pwg->length);
+
+ testBegin("pwgMediaForLegacy(\"na-letter\")");
+ if ((pwg = pwgMediaForLegacy("na-letter")) == NULL)
+ testEndMessage(false, "not found");
+ else if (strcmp(pwg->pwg, "na_letter_8.5x11in"))
+ testEndMessage(false, "%s", pwg->pwg);
+ else if (pwg->width != 21590 || pwg->length != 27940)
+ testEndMessage(false, "%dx%d", pwg->width, pwg->length);
else
- puts("PASS");
-
- fputs("pwgMediaForPPD(\"4x6\"): ", stdout);
- if ((pwgmedia = pwgMediaForPPD("4x6")) == NULL)
- {
- puts("FAIL (not found)");
- status ++;
- }
- else if (strcmp(pwgmedia->pwg, "na_index-4x6_4x6in"))
- {
- printf("FAIL (%s)\n", pwgmedia->pwg);
- status ++;
- }
- else if (pwgmedia->width != 10160 || pwgmedia->length != 15240)
- {
- printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
- status ++;
- }
+ testEnd(true);
+
+ testBegin("pwgMediaForPPD(\"4x6\")");
+ if ((pwg = pwgMediaForPPD("4x6")) == NULL)
+ testEndMessage(false, "not found");
+ else if (strcmp(pwg->pwg, "na_index-4x6_4x6in"))
+ testEndMessage(false, "%s", pwg->pwg);
+ else if (pwg->width != 10160 || pwg->length != 15240)
+ testEndMessage(false, "%dx%d", pwg->width, pwg->length);
else
- puts("PASS");
-
- fputs("pwgMediaForPPD(\"10x15cm\"): ", stdout);
- if ((pwgmedia = pwgMediaForPPD("10x15cm")) == NULL)
- {
- puts("FAIL (not found)");
- status ++;
- }
- else if (strcmp(pwgmedia->pwg, "om_100x150mm_100x150mm"))
- {
- printf("FAIL (%s)\n", pwgmedia->pwg);
- status ++;
- }
- else if (pwgmedia->width != 10000 || pwgmedia->length != 15000)
- {
- printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
- status ++;
- }
+ testEnd(true);
+
+ testBegin("pwgMediaForPPD(\"10x15cm\")");
+ if ((pwg = pwgMediaForPPD("10x15cm")) == NULL)
+ testEndMessage(false, "not found");
+ else if (strcmp(pwg->pwg, "om_100x150mm_100x150mm"))
+ testEndMessage(false, "%s", pwg->pwg);
+ else if (pwg->width != 10000 || pwg->length != 15000)
+ testEndMessage(false, "%dx%d", pwg->width, pwg->length);
else
- puts("PASS");
-
- fputs("pwgMediaForPPD(\"Custom.10x15cm\"): ", stdout);
- if ((pwgmedia = pwgMediaForPPD("Custom.10x15cm")) == NULL)
- {
- puts("FAIL (not found)");
- status ++;
- }
- else if (strcmp(pwgmedia->pwg, "custom_10x15cm_100x150mm"))
- {
- printf("FAIL (%s)\n", pwgmedia->pwg);
- status ++;
- }
- else if (pwgmedia->width != 10000 || pwgmedia->length != 15000)
- {
- printf("FAIL (%dx%d)\n", pwgmedia->width, pwgmedia->length);
- status ++;
- }
+ testEnd(true);
+
+ testBegin("pwgMediaForPPD(\"Custom.10x15cm\")");
+ if ((pwg = pwgMediaForPPD("Custom.10x15cm")) == NULL)
+ testEndMessage(false, "not found");
+ else if (strcmp(pwg->pwg, "custom_10x15cm_100x150mm"))
+ testEndMessage(false, "%s", pwg->pwg);
+ else if (pwg->width != 10000 || pwg->length != 15000)
+ testEndMessage(false, "%dx%d", pwg->width, pwg->length);
else
- puts("PASS");
+ testEnd(true);
- fputs("pwgMediaForSize(29700, 42000): ", stdout);
- if ((pwgmedia = pwgMediaForSize(29700, 42000)) == NULL)
- {
- puts("FAIL (not found)");
- status ++;
- }
- else if (strcmp(pwgmedia->pwg, "iso_a3_297x420mm"))
- {
- printf("FAIL (%s)\n", pwgmedia->pwg);
- status ++;
- }
+ testBegin("pwgMediaForSize(29700, 42000)");
+ if ((pwg = pwgMediaForSize(29700, 42000)) == NULL)
+ testEndMessage(false, "not found");
+ else if (strcmp(pwg->pwg, "iso_a3_297x420mm"))
+ testEndMessage(false, "%s", pwg->pwg);
else
- puts("PASS");
+ testEnd(true);
- fputs("pwgMediaForSize(9842, 19050): ", stdout);
- if ((pwgmedia = pwgMediaForSize(9842, 19050)) == NULL)
- {
- puts("FAIL (not found)");
- status ++;
- }
- else if (strcmp(pwgmedia->pwg, "na_monarch_3.875x7.5in"))
- {
- printf("FAIL (%s)\n", pwgmedia->pwg);
- status ++;
- }
+ testBegin("pwgMediaForSize(9842, 19050)");
+ if ((pwg = pwgMediaForSize(9842, 19050)) == NULL)
+ testEndMessage(false, "not found");
+ else if (strcmp(pwg->pwg, "na_monarch_3.875x7.5in"))
+ testEndMessage(false, "%s", pwg->pwg);
else
- printf("PASS (%s)\n", pwgmedia->pwg);
+ testEndMessage(true, "%s", pwg->pwg);
- fputs("pwgMediaForSize(9800, 19000): ", stdout);
- if ((pwgmedia = pwgMediaForSize(9800, 19000)) == NULL)
- {
- puts("FAIL (not found)");
- status ++;
- }
- else if (strcmp(pwgmedia->pwg, "jpn_you6_98x190mm"))
- {
- printf("FAIL (%s)\n", pwgmedia->pwg);
- status ++;
- }
+ testBegin("pwgMediaForSize(9800, 19000)");
+ if ((pwg = pwgMediaForSize(9800, 19000)) == NULL)
+ testEndMessage(false, "not found");
+ else if (strcmp(pwg->pwg, "jpn_you6_98x190mm"))
+ testEndMessage(false, "%s", pwg->pwg);
else
- printf("PASS (%s)\n", pwgmedia->pwg);
+ testEndMessage(true, "%s", pwg->pwg);
- fputs("Duplicate size test: ", stdout);
- for (mediatable = _pwgMediaTable(&num_media);
- num_media > 1;
- num_media --, mediatable ++)
+ testBegin("Duplicate size test");
+ for (mediatable = _pwgMediaTable(&num_media); num_media > 1; num_media --, mediatable ++)
{
- for (i = num_media - 1, pwgmedia = mediatable + 1; i > 0; i --, pwgmedia ++)
+ for (i = num_media - 1, pwg = mediatable + 1; i > 0; i --, pwg ++)
{
- if (pwgmedia->width == mediatable->width &&
- pwgmedia->length == mediatable->length)
+ if (pwg->width == mediatable->width && pwg->length == mediatable->length)
{
- if (!dupmedia)
- {
- dupmedia = 1;
- status ++;
- puts("FAIL");
- }
-
- printf(" %s and %s have the same dimensions (%dx%d)\n",
- pwgmedia->pwg, mediatable->pwg, pwgmedia->width,
- pwgmedia->length);
+ testEndMessage(false, "%s and %s have the same dimensions (%dx%d)", pwg->pwg, mediatable->pwg, pwg->width, pwg->length);
+ break;
}
}
- }
- if (!dupmedia)
- puts("PASS");
-
-
- return (status);
-}
-
-
-/*
- * 'test_pagesize()' - Test the PWG mapping functions.
- */
-
-static int /* O - 1 on failure, 0 on success */
-test_pagesize(_ppd_cache_t *pc, /* I - PWG mapping data */
- ppd_file_t *ppd, /* I - PPD file */
- const char *ppdsize) /* I - PPD page size */
-{
- int status = 0; /* Return status */
- ipp_t *job; /* Job attributes */
- const char *pagesize; /* PageSize value */
-
-
- if (ppdPageSize(ppd, ppdsize))
- {
- printf("_ppdCacheGetPageSize(keyword=%s): ", ppdsize);
- fflush(stdout);
-
- if ((pagesize = _ppdCacheGetPageSize(pc, NULL, ppdsize, NULL)) == NULL)
- {
- puts("FAIL (Not Found)");
- status = 1;
- }
- else if (_cups_strcasecmp(pagesize, ppdsize))
- {
- printf("FAIL (Got \"%s\", Expected \"%s\")\n", pagesize, ppdsize);
- status = 1;
- }
- else
- puts("PASS");
- job = ippNew();
- ippAddString(job, IPP_TAG_JOB, IPP_TAG_KEYWORD, "media", NULL, ppdsize);
-
- printf("_ppdCacheGetPageSize(media=%s): ", ppdsize);
- fflush(stdout);
-
- if ((pagesize = _ppdCacheGetPageSize(pc, job, NULL, NULL)) == NULL)
- {
- puts("FAIL (Not Found)");
- status = 1;
- }
- else if (_cups_strcasecmp(pagesize, ppdsize))
- {
- printf("FAIL (Got \"%s\", Expected \"%s\")\n", pagesize, ppdsize);
- status = 1;
- }
- else
- puts("PASS");
-
- ippDelete(job);
+ if (i > 0)
+ break;
}
- return (status);
-}
-
-
-/*
- * 'test_ppd_cache()' - Test the PPD cache functions.
- */
-
-static int /* O - 1 on failure, 0 on success */
-test_ppd_cache(_ppd_cache_t *pc, /* I - PWG mapping data */
- ppd_file_t *ppd) /* I - PPD file */
-{
- int i, /* Looping var */
- status = 0; /* Return status */
- _ppd_cache_t *pc2; /* Loaded data */
- pwg_size_t *size, /* Size from original */
- *size2; /* Size from saved */
- pwg_map_t *map, /* Map from original */
- *map2; /* Map from saved */
-
-
- /*
- * Verify that we can write and read back the same data...
- */
-
- fputs("_ppdCacheWriteFile(test.pwg): ", stdout);
- if (!_ppdCacheWriteFile(pc, "test.pwg", NULL))
- {
- puts("FAIL");
- status ++;
- }
- else
- puts("PASS");
-
- fputs("_ppdCacheCreateWithFile(test.pwg): ", stdout);
- if ((pc2 = _ppdCacheCreateWithFile("test.pwg", NULL)) == NULL)
- {
- puts("FAIL");
- status ++;
- }
- else
- {
- // TODO: FINISH ADDING ALL VALUES IN STRUCTURE
- if (pc2->num_sizes != pc->num_sizes)
- {
- if (!status)
- puts("FAIL");
-
- printf(" SAVED num_sizes=%d, ORIG num_sizes=%d\n", pc2->num_sizes,
- pc->num_sizes);
-
- status ++;
- }
- else
- {
- for (i = pc->num_sizes, size = pc->sizes, size2 = pc2->sizes;
- i > 0;
- i --, size ++, size2 ++)
- {
- if (strcmp(size2->map.pwg, size->map.pwg) ||
- strcmp(size2->map.ppd, size->map.ppd) ||
- size2->width != size->width ||
- size2->length != size->length ||
- size2->left != size->left ||
- size2->bottom != size->bottom ||
- size2->right != size->right ||
- size2->top != size->top)
- {
- if (!status)
- puts("FAIL");
-
- if (strcmp(size->map.pwg, size2->map.pwg))
- printf(" SAVED size->map.pwg=\"%s\", ORIG "
- "size->map.pwg=\"%s\"\n", size2->map.pwg, size->map.pwg);
-
- if (strcmp(size2->map.ppd, size->map.ppd))
- printf(" SAVED size->map.ppd=\"%s\", ORIG "
- "size->map.ppd=\"%s\"\n", size2->map.ppd, size->map.ppd);
-
- if (size2->width != size->width)
- printf(" SAVED size->width=%d, ORIG size->width=%d\n",
- size2->width, size->width);
-
- if (size2->length != size->length)
- printf(" SAVED size->length=%d, ORIG size->length=%d\n",
- size2->length, size->length);
-
- if (size2->left != size->left)
- printf(" SAVED size->left=%d, ORIG size->left=%d\n",
- size2->left, size->left);
-
- if (size2->bottom != size->bottom)
- printf(" SAVED size->bottom=%d, ORIG size->bottom=%d\n",
- size2->bottom, size->bottom);
-
- if (size2->right != size->right)
- printf(" SAVED size->right=%d, ORIG size->right=%d\n",
- size2->right, size->right);
-
- if (size2->top != size->top)
- printf(" SAVED size->top=%d, ORIG size->top=%d\n",
- size2->top, size->top);
-
- status ++;
- break;
- }
- }
-
- for (i = pc->num_sources, map = pc->sources, map2 = pc2->sources;
- i > 0;
- i --, map ++, map2 ++)
- {
- if (strcmp(map2->pwg, map->pwg) ||
- strcmp(map2->ppd, map->ppd))
- {
- if (!status)
- puts("FAIL");
-
- if (strcmp(map->pwg, map2->pwg))
- printf(" SAVED source->pwg=\"%s\", ORIG source->pwg=\"%s\"\n",
- map2->pwg, map->pwg);
-
- if (strcmp(map2->ppd, map->ppd))
- printf(" SAVED source->ppd=\"%s\", ORIG source->ppd=\"%s\"\n",
- map2->ppd, map->ppd);
-
- status ++;
- break;
- }
- }
-
- for (i = pc->num_types, map = pc->types, map2 = pc2->types;
- i > 0;
- i --, map ++, map2 ++)
- {
- if (strcmp(map2->pwg, map->pwg) ||
- strcmp(map2->ppd, map->ppd))
- {
- if (!status)
- puts("FAIL");
-
- if (strcmp(map->pwg, map2->pwg))
- printf(" SAVED type->pwg=\"%s\", ORIG type->pwg=\"%s\"\n",
- map2->pwg, map->pwg);
-
- if (strcmp(map2->ppd, map->ppd))
- printf(" SAVED type->ppd=\"%s\", ORIG type->ppd=\"%s\"\n",
- map2->ppd, map->ppd);
-
- status ++;
- break;
- }
- }
- }
-
- if (!status)
- puts("PASS");
-
- _ppdCacheDestroy(pc2);
- }
-
- /*
- * Test PageSize mapping code...
- */
-
- status += test_pagesize(pc, ppd, "Letter");
- status += test_pagesize(pc, ppd, "na-letter");
- status += test_pagesize(pc, ppd, "A4");
- status += test_pagesize(pc, ppd, "iso-a4");
+ if (num_media == 1)
+ testEnd(true);
- return (status);
+ return (testsPassed ? 0 : 1);
}
diff --git a/cups/tls-gnutls.c b/cups/tls-gnutls.c
index 92f856e54..1a27f0885 100644
--- a/cups/tls-gnutls.c
+++ b/cups/tls-gnutls.c
@@ -1,174 +1,216 @@
-/*
- * TLS support code for CUPS using GNU TLS.
- *
- * Copyright © 2020-2022 by OpenPrinting
- * Copyright © 2007-2019 by Apple Inc.
- * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
- *
- * Licensed under Apache License v2.0. See the file "LICENSE" for more
- * information.
- */
-
-/**** This file is included from tls.c ****/
-
-/*
- * Include necessary headers...
- */
-
-#include
-
-
-/*
- * Local globals...
- */
-
-static int tls_auto_create = 0;
- /* Auto-create self-signed certs? */
-static char *tls_common_name = NULL;
- /* Default common name */
+//
+// TLS support code for CUPS using GNU TLS.
+//
+// Copyright © 2020-2023 by OpenPrinting
+// Copyright © 2007-2019 by Apple Inc.
+// Copyright © 1997-2007 by Easy Software Products, all rights reserved.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
+//// This file is included from tls.c
+
+
+//
+// Local functions...
+//
+
+static gnutls_x509_crt_t gnutls_create_credential(http_credential_t *credential);
+static gnutls_x509_privkey_t gnutls_create_key(cups_credtype_t type);
+static ssize_t gnutls_http_read(gnutls_transport_ptr_t ptr, void *data, size_t length);
+static ssize_t gnutls_http_write(gnutls_transport_ptr_t ptr, const void *data, size_t length);
+static void gnutls_load_crl(void);
+
+
+//
+// Local globals...
+//
+
static gnutls_x509_crl_t tls_crl = NULL;/* Certificate revocation list */
-static char *tls_keypath = NULL;
- /* Server cert keychain path */
-static cups_mutex_t tls_mutex = CUPS_MUTEX_INITIALIZER;
- /* Mutex for keychain/certs */
-static int tls_options = -1,/* Options for TLS connections */
- tls_min_version = _HTTP_TLS_1_0,
- tls_max_version = _HTTP_TLS_MAX;
-
-
-/*
- * Local functions...
- */
-
-static gnutls_x509_crt_t http_gnutls_create_credential(http_credential_t *credential);
-static const char *http_gnutls_default_path(char *buffer, size_t bufsize);
-static void http_gnutls_load_crl(void);
-static const char *http_gnutls_make_path(char *buffer, size_t bufsize, const char *dirname, const char *filename, const char *ext);
-static ssize_t http_gnutls_read(gnutls_transport_ptr_t ptr, void *data, size_t length);
-static ssize_t http_gnutls_write(gnutls_transport_ptr_t ptr, const void *data, size_t length);
-
-
-/*
- * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
- */
-
-int /* O - 1 on success, 0 on failure */
-cupsMakeServerCredentials(
- const char *path, /* I - Path to keychain/directory */
- const char *common_name, /* I - Common name */
- int num_alt_names, /* I - Number of subject alternate names */
- const char **alt_names, /* I - Subject Alternate Names */
- time_t expiration_date) /* I - Expiration date */
-{
- gnutls_x509_crt_t crt; /* Self-signed certificate */
- gnutls_x509_privkey_t key; /* Encryption private key */
- char temp[1024], /* Temporary directory name */
- crtfile[1024], /* Certificate filename */
- keyfile[1024]; /* Private key filename */
- cups_lang_t *language; /* Default language info */
- const char *langname; /* Default language name */
- cups_file_t *fp; /* Key/cert file */
- unsigned char buffer[8192]; /* Buffer for x509 data */
- size_t bytes; /* Number of bytes of data */
- unsigned char serial[4]; /* Serial number buffer */
- time_t curtime; /* Current time */
- int result; /* Result of GNU TLS calls */
-
-
- DEBUG_printf(("cupsMakeServerCredentials(path=\"%s\", common_name=\"%s\", num_alt_names=%d, alt_names=%p, expiration_date=%d)", path, common_name, num_alt_names, alt_names, (int)expiration_date));
-
- /*
- * Filenames...
- */
+
+//
+// 'cupsCreateCredentials()' - Make an X.509 certificate and private key pair.
+//
+// This function creates an X.509 certificate and private key pair. The
+// certificate and key are stored in the directory "path" or, if "path" is
+// `NULL`, in a per-user or system-wide (when running as root) certificate/key
+// store. The generated certificate is signed by the named root certificate or,
+// if "root_name" is `NULL`, a site-wide default root certificate. When
+// "root_name" is `NULL` and there is no site-wide default root certificate, a
+// self-signed certificate is generated instead.
+//
+// The "ca_cert" argument specifies whether a CA certificate should be created.
+//
+// The "purpose" argument specifies the purpose(s) used for the credentials as a
+// bitwise OR of the following constants:
+//
+// - `CUPS_CREDPURPOSE_SERVER_AUTH` for validating TLS servers,
+// - `CUPS_CREDPURPOSE_CLIENT_AUTH` for validating TLS clients,
+// - `CUPS_CREDPURPOSE_CODE_SIGNING` for validating compiled code,
+// - `CUPS_CREDPURPOSE_EMAIL_PROTECTION` for validating email messages,
+// - `CUPS_CREDPURPOSE_TIME_STAMPING` for signing timestamps to objects, and/or
+// - `CUPS_CREDPURPOSE_OCSP_SIGNING` for Online Certificate Status Protocol
+// message signing.
+//
+// The "type" argument specifies the type of credentials using one of the
+// following constants:
+//
+// - `CUPS_CREDTYPE_DEFAULT`: default type (RSA-3072 or P-384),
+// - `CUPS_CREDTYPE_RSA_2048_SHA256`: RSA with 2048-bit keys and SHA-256 hash,
+// - `CUPS_CREDTYPE_RSA_3072_SHA256`: RSA with 3072-bit keys and SHA-256 hash,
+// - `CUPS_CREDTYPE_RSA_4096_SHA256`: RSA with 4096-bit keys and SHA-256 hash,
+// - `CUPS_CREDTYPE_ECDSA_P256_SHA256`: ECDSA using the P-256 curve with SHA-256 hash,
+// - `CUPS_CREDTYPE_ECDSA_P384_SHA256`: ECDSA using the P-384 curve with SHA-256 hash, or
+// - `CUPS_CREDTYPE_ECDSA_P521_SHA256`: ECDSA using the P-521 curve with SHA-256 hash.
+//
+// The "usage" argument specifies the usage(s) for the credentials as a bitwise
+// OR of the following constants:
+//
+// - `CUPS_CREDUSAGE_DIGITAL_SIGNATURE`: digital signatures,
+// - `CUPS_CREDUSAGE_NON_REPUDIATION`: non-repudiation/content commitment,
+// - `CUPS_CREDUSAGE_KEY_ENCIPHERMENT`: key encipherment,
+// - `CUPS_CREDUSAGE_DATA_ENCIPHERMENT`: data encipherment,
+// - `CUPS_CREDUSAGE_KEY_AGREEMENT`: key agreement,
+// - `CUPS_CREDUSAGE_KEY_CERT_SIGN`: key certicate signing,
+// - `CUPS_CREDUSAGE_CRL_SIGN`: certificate revocation list signing,
+// - `CUPS_CREDUSAGE_ENCIPHER_ONLY`: encipherment only,
+// - `CUPS_CREDUSAGE_DECIPHER_ONLY`: decipherment only,
+// - `CUPS_CREDUSAGE_DEFAULT_CA`: defaults for CA certificates,
+// - `CUPS_CREDUSAGE_DEFAULT_TLS`: defaults for TLS certificates, and/or
+// - `CUPS_CREDUSAGE_ALL`: all usages.
+//
+// The "organization", "org_unit", "locality", "state_province", and "country"
+// arguments specify information about the identity and geolocation of the
+// issuer.
+//
+// The "common_name" argument specifies the common name and the "num_alt_names"
+// and "alt_names" arguments specify a list of DNS hostnames for the
+// certificate.
+//
+// The "expiration_date" argument specifies the expiration date and time as a
+// Unix `time_t` value in seconds.
+//
+
+bool // O - `true` on success, `false` on error
+cupsCreateCredentials(
+ const char *path, // I - Directory path for certificate/key store or `NULL` for default
+ bool ca_cert, // I - `true` to create a CA certificate, `false` for a client/server certificate
+ cups_credpurpose_t purpose, // I - Credential purposes
+ cups_credtype_t type, // I - Credential type
+ cups_credusage_t usage, // I - Credential usages
+ const char *organization, // I - Organization or `NULL` to use common name
+ const char *org_unit, // I - Organizational unit or `NULL` for none
+ const char *locality, // I - City/town or `NULL` for "Unknown"
+ const char *state_province, // I - State/province or `NULL` for "Unknown"
+ const char *country, // I - Country or `NULL` for locale-based default
+ const char *common_name, // I - Common name
+ size_t num_alt_names, // I - Number of subject alternate names
+ const char * const *alt_names, // I - Subject Alternate Names
+ const char *root_name, // I - Root certificate/domain name or `NULL` for site/self-signed
+ time_t expiration_date) // I - Expiration date
+{
+ bool ret = false; // Return value
+ gnutls_x509_crt_t crt = NULL; // New certificate
+ gnutls_x509_privkey_t key = NULL; // Encryption private key
+ gnutls_x509_crt_t root_crt = NULL;// Root certificate
+ gnutls_x509_privkey_t root_key = NULL;// Root private key
+ char temp[1024], // Temporary directory name
+ crtfile[1024], // Certificate filename
+ keyfile[1024], // Private key filename
+ *root_crtdata, // Root certificate data
+ *root_keydata; // Root private key data
+ unsigned gnutls_usage = 0;// GNU TLS keyUsage bits
+ cups_file_t *fp; // Key/cert file
+ unsigned char buffer[65536]; // Buffer for x509 data
+ size_t bytes; // Number of bytes of data
+ unsigned char serial[4]; // Serial number buffer
+ time_t curtime; // Current time
+ int err; // Result of GNU TLS calls
+
+
+ DEBUG_printf(("cupsCreateCredentials(path=\"%s\", ca_cert=%s, purpose=0x%x, type=%d, usage=0x%x, organization=\"%s\", org_unit=\"%s\", locality=\"%s\", state_province=\"%s\", country=\"%s\", common_name=\"%s\", num_alt_names=%u, alt_names=%p, root_name=\"%s\", expiration_date=%ld)", path, ca_cert ? "true" : "false", purpose, type, usage, organization, org_unit, locality, state_province, country, common_name, (unsigned)num_alt_names, alt_names, root_name, (long)expiration_date));
+
+ // Filenames...
if (!path)
- path = http_gnutls_default_path(temp, sizeof(temp));
+ path = http_default_path(temp, sizeof(temp));
if (!path || !common_name)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
- return (0);
+ goto done;
}
- http_gnutls_make_path(crtfile, sizeof(crtfile), path, common_name, "crt");
- http_gnutls_make_path(keyfile, sizeof(keyfile), path, common_name, "key");
-
- /*
- * Create the encryption key...
- */
-
- DEBUG_puts("1cupsMakeServerCredentials: Creating key pair.");
+ http_make_path(crtfile, sizeof(crtfile), path, common_name, "crt");
+ http_make_path(keyfile, sizeof(keyfile), path, common_name, "key");
- gnutls_x509_privkey_init(&key);
- gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 3072, 0);
+ // Create the encryption key...
+ DEBUG_puts("1cupsCreateCredentials: Creating key pair.");
- DEBUG_puts("1cupsMakeServerCredentials: Key pair created.");
+ key = gnutls_create_key(type);
- /*
- * Save it...
- */
+ DEBUG_puts("1cupsCreateCredentials: Key pair created.");
+ // Save it...
bytes = sizeof(buffer);
- if ((result = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0)
+ if ((err = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0)
{
- DEBUG_printf(("1cupsMakeServerCredentials: Unable to export private key: %s", gnutls_strerror(result)));
- _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(result), 0);
- gnutls_x509_privkey_deinit(key);
- return (0);
+ DEBUG_printf(("1cupsCreateCredentials: Unable to export private key: %s", gnutls_strerror(err)));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(err), 0);
+ goto done;
}
else if ((fp = cupsFileOpen(keyfile, "w")) != NULL)
{
- DEBUG_printf(("1cupsMakeServerCredentials: Writing private key to \"%s\".", keyfile));
+ DEBUG_printf(("1cupsCreateCredentials: Writing private key to \"%s\".", keyfile));
cupsFileWrite(fp, (char *)buffer, bytes);
cupsFileClose(fp);
}
else
{
- DEBUG_printf(("1cupsMakeServerCredentials: Unable to create private key file \"%s\": %s", keyfile, strerror(errno)));
+ DEBUG_printf(("1cupsCreateCredentials: Unable to create private key file \"%s\": %s", keyfile, strerror(errno)));
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
- gnutls_x509_privkey_deinit(key);
- return (0);
+ goto done;
}
- /*
- * Create the self-signed certificate...
- */
-
- DEBUG_puts("1cupsMakeServerCredentials: Generating self-signed X.509 certificate.");
+ // Create the certificate...
+ DEBUG_puts("1cupsCreateCredentials: Generating X.509 certificate.");
- language = cupsLangDefault();
- langname = cupsLangGetName(language);
curtime = time(NULL);
serial[0] = (unsigned char)(curtime >> 24);
serial[1] = (unsigned char)(curtime >> 16);
serial[2] = (unsigned char)(curtime >> 8);
serial[3] = (unsigned char)(curtime);
+ if (!organization)
+ organization = common_name;
+ if (!org_unit)
+ org_unit = "";
+ if (!locality)
+ locality = "Unknown";
+ if (!state_province)
+ state_province = "Unknown";
+ if (!country)
+ country = "US";
+
gnutls_x509_crt_init(&crt);
- if (strlen(langname) == 5)
- gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, langname + 3, 2);
- else
- gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, "US", 2);
+ gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, "US", 2);
gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0, common_name, strlen(common_name));
- gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, common_name, strlen(common_name));
- gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, "Unknown", 7);
- gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, "Unknown", 7);
- gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0, "Unknown", 7);
-/* gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_PKCS9_EMAIL, 0, ServerAdmin, strlen(ServerAdmin));*/
+ gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, organization, strlen(organization));
+ gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, org_unit, strlen(org_unit));
+ gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, state_province, strlen(state_province));
+ gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0, locality, strlen(locality));
gnutls_x509_crt_set_key(crt, key);
gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
gnutls_x509_crt_set_activation_time(crt, curtime);
gnutls_x509_crt_set_expiration_time(crt, expiration_date);
- gnutls_x509_crt_set_ca_status(crt, 0);
+ gnutls_x509_crt_set_ca_status(crt, ca_cert ? 1 : 0);
gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, common_name, (unsigned)strlen(common_name), GNUTLS_FSAN_SET);
if (!strchr(common_name, '.'))
{
- /*
- * Add common_name.local to the list, too...
- */
-
- char localname[256]; /* hostname.local */
+ // Add common_name.local to the list, too...
+ char localname[256]; // hostname.local
snprintf(localname, sizeof(localname), "%s.local", common_name);
gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, localname, (unsigned)strlen(localname), GNUTLS_FSAN_APPEND);
@@ -176,7 +218,7 @@ cupsMakeServerCredentials(
gnutls_x509_crt_set_subject_alt_name(crt, GNUTLS_SAN_DNSNAME, "localhost", 9, GNUTLS_FSAN_APPEND);
if (num_alt_names > 0)
{
- int i; /* Looping var */
+ size_t i; // Looping var
for (i = 0; i < num_alt_names; i ++)
{
@@ -186,130 +228,761 @@ cupsMakeServerCredentials(
}
}
}
- gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
- gnutls_x509_crt_set_key_usage(crt, GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT);
+
+ if (purpose & CUPS_CREDPURPOSE_SERVER_AUTH)
+ gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
+ if (purpose & CUPS_CREDPURPOSE_CLIENT_AUTH)
+ gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_CLIENT, 0);
+ if (purpose & CUPS_CREDPURPOSE_CODE_SIGNING)
+ gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_CODE_SIGNING, 0);
+ if (purpose & CUPS_CREDPURPOSE_EMAIL_PROTECTION)
+ gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_EMAIL_PROTECTION, 0);
+ if (purpose & CUPS_CREDPURPOSE_OCSP_SIGNING)
+ gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_OCSP_SIGNING, 0);
+
+ if (usage & CUPS_CREDUSAGE_DIGITAL_SIGNATURE)
+ gnutls_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE;
+ if (usage & CUPS_CREDUSAGE_NON_REPUDIATION)
+ gnutls_usage |= GNUTLS_KEY_NON_REPUDIATION;
+ if (usage & CUPS_CREDUSAGE_KEY_ENCIPHERMENT)
+ gnutls_usage |= GNUTLS_KEY_KEY_ENCIPHERMENT;
+ if (usage & CUPS_CREDUSAGE_DATA_ENCIPHERMENT)
+ gnutls_usage |= GNUTLS_KEY_DATA_ENCIPHERMENT;
+ if (usage & CUPS_CREDUSAGE_KEY_AGREEMENT)
+ gnutls_usage |= GNUTLS_KEY_KEY_AGREEMENT;
+ if (usage & CUPS_CREDUSAGE_KEY_CERT_SIGN)
+ gnutls_usage |= GNUTLS_KEY_KEY_CERT_SIGN;
+ if (usage & CUPS_CREDUSAGE_CRL_SIGN)
+ gnutls_usage |= GNUTLS_KEY_CRL_SIGN;
+ if (usage & CUPS_CREDUSAGE_ENCIPHER_ONLY)
+ gnutls_usage |= GNUTLS_KEY_ENCIPHER_ONLY;
+ if (usage & CUPS_CREDUSAGE_DECIPHER_ONLY)
+ gnutls_usage |= GNUTLS_KEY_DECIPHER_ONLY;
+
+ gnutls_x509_crt_set_key_usage(crt, gnutls_usage);
gnutls_x509_crt_set_version(crt, 3);
bytes = sizeof(buffer);
if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0)
gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes);
- gnutls_x509_crt_sign(crt, crt, key);
+ // Try loading a root certificate...
+ if (!ca_cert)
+ {
+ root_crtdata = cupsCopyCredentials(path, root_name ? root_name : "_site_");
+ root_keydata = cupsCopyCredentialsKey(path, root_name ? root_name : "_site_");
+
+ if (root_crtdata && root_keydata)
+ {
+ // Load root certificate...
+ gnutls_datum_t datum; // Datum for cert/key
+
+ datum.data = (unsigned char *)root_crtdata;
+ datum.size = strlen(root_crtdata);
+
+ gnutls_x509_crt_init(&root_crt);
+ if (gnutls_x509_crt_import(root_crt, &datum, GNUTLS_X509_FMT_PEM) < 0)
+ {
+ // No good, clear it...
+ gnutls_x509_crt_deinit(root_crt);
+ root_crt = NULL;
+ }
+ else
+ {
+ // Load root private key...
+ datum.data = (unsigned char *)root_keydata;
+ datum.size = strlen(root_keydata);
+
+ gnutls_x509_privkey_init(&root_key);
+ if (gnutls_x509_privkey_import(root_key, &datum, GNUTLS_X509_FMT_PEM) < 0)
+ {
+ // No food, clear them...
+ gnutls_x509_privkey_deinit(root_key);
+ root_key = NULL;
+
+ gnutls_x509_crt_deinit(root_crt);
+ root_crt = NULL;
+ }
+ }
+ }
+
+ free(root_crtdata);
+ free(root_keydata);
+ }
- /*
- * Save it...
- */
+ if (root_crt && root_key)
+ {
+ gnutls_x509_crt_sign(crt, root_crt, root_key);
+ gnutls_x509_crt_deinit(root_crt);
+ gnutls_x509_privkey_deinit(root_key);
+ }
+ else
+ {
+ gnutls_x509_crt_sign(crt, crt, key);
+ }
+ // Save it...
bytes = sizeof(buffer);
- if ((result = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0)
+ if ((err = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0)
{
- DEBUG_printf(("1cupsMakeServerCredentials: Unable to export public key and X.509 certificate: %s", gnutls_strerror(result)));
- _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(result), 0);
- gnutls_x509_crt_deinit(crt);
- gnutls_x509_privkey_deinit(key);
- return (0);
+ DEBUG_printf(("1cupsCreateCredentials: Unable to export public key and X.509 certificate: %s", gnutls_strerror(err)));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(err), 0);
+ goto done;
}
else if ((fp = cupsFileOpen(crtfile, "w")) != NULL)
{
- DEBUG_printf(("1cupsMakeServerCredentials: Writing public key and X.509 certificate to \"%s\".", crtfile));
+ DEBUG_printf(("1cupsCreateCredentials: Writing public key and X.509 certificate to \"%s\".", crtfile));
cupsFileWrite(fp, (char *)buffer, bytes);
cupsFileClose(fp);
}
else
{
- DEBUG_printf(("1cupsMakeServerCredentials: Unable to create public key and X.509 certificate file \"%s\": %s", crtfile, strerror(errno)));
+ DEBUG_printf(("1cupsCreateCredentials: Unable to create public key and X.509 certificate file \"%s\": %s", crtfile, strerror(errno)));
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
- gnutls_x509_crt_deinit(crt);
- gnutls_x509_privkey_deinit(key);
- return (0);
+ goto done;
}
- /*
- * Cleanup...
- */
+ DEBUG_puts("1cupsCreateCredentials: Successfully created credentials.");
- gnutls_x509_crt_deinit(crt);
- gnutls_x509_privkey_deinit(key);
+ ret = true;
- DEBUG_puts("1cupsMakeServerCredentials: Successfully created credentials.");
+ // Cleanup...
+ done:
- return (1);
+ if (crt)
+ gnutls_x509_crt_deinit(crt);
+ if (key)
+ gnutls_x509_privkey_deinit(key);
+
+ return (ret);
}
-/*
- * 'cupsSetServerCredentials()' - Set the default server credentials.
- *
- * Note: The server credentials are used by all threads in the running process.
- * This function is threadsafe.
- */
+//
+// 'cupsCreateCredentialsRequest()' - Make an X.509 Certificate Signing Request.
+//
+// This function creates an X.509 certificate signing request (CSR) and
+// associated private key. The CSR and key are stored in the directory "path"
+// or, if "path" is `NULL`, in a per-user or system-wide (when running as root)
+// certificate/key store.
+//
+// The "purpose" argument specifies the purpose(s) used for the credentials as a
+// bitwise OR of the following constants:
+//
+// - `CUPS_CREDPURPOSE_SERVER_AUTH` for validating TLS servers,
+// - `CUPS_CREDPURPOSE_CLIENT_AUTH` for validating TLS clients,
+// - `CUPS_CREDPURPOSE_CODE_SIGNING` for validating compiled code,
+// - `CUPS_CREDPURPOSE_EMAIL_PROTECTION` for validating email messages,
+// - `CUPS_CREDPURPOSE_TIME_STAMPING` for signing timestamps to objects, and/or
+// - `CUPS_CREDPURPOSE_OCSP_SIGNING` for Online Certificate Status Protocol
+// message signing.
+//
+// The "type" argument specifies the type of credentials using one of the
+// following constants:
+//
+// - `CUPS_CREDTYPE_DEFAULT`: default type (RSA-3072 or P-384),
+// - `CUPS_CREDTYPE_RSA_2048_SHA256`: RSA with 2048-bit keys and SHA-256 hash,
+// - `CUPS_CREDTYPE_RSA_3072_SHA256`: RSA with 3072-bit keys and SHA-256 hash,
+// - `CUPS_CREDTYPE_RSA_4096_SHA256`: RSA with 4096-bit keys and SHA-256 hash,
+// - `CUPS_CREDTYPE_ECDSA_P256_SHA256`: ECDSA using the P-256 curve with SHA-256 hash,
+// - `CUPS_CREDTYPE_ECDSA_P384_SHA256`: ECDSA using the P-384 curve with SHA-256 hash, or
+// - `CUPS_CREDTYPE_ECDSA_P521_SHA256`: ECDSA using the P-521 curve with SHA-256 hash.
+//
+// The "usage" argument specifies the usage(s) for the credentials as a bitwise
+// OR of the following constants:
+//
+// - `CUPS_CREDUSAGE_DIGITAL_SIGNATURE`: digital signatures,
+// - `CUPS_CREDUSAGE_NON_REPUDIATION`: non-repudiation/content commitment,
+// - `CUPS_CREDUSAGE_KEY_ENCIPHERMENT`: key encipherment,
+// - `CUPS_CREDUSAGE_DATA_ENCIPHERMENT`: data encipherment,
+// - `CUPS_CREDUSAGE_KEY_AGREEMENT`: key agreement,
+// - `CUPS_CREDUSAGE_KEY_CERT_SIGN`: key certicate signing,
+// - `CUPS_CREDUSAGE_CRL_SIGN`: certificate revocation list signing,
+// - `CUPS_CREDUSAGE_ENCIPHER_ONLY`: encipherment only,
+// - `CUPS_CREDUSAGE_DECIPHER_ONLY`: decipherment only,
+// - `CUPS_CREDUSAGE_DEFAULT_CA`: defaults for CA certificates,
+// - `CUPS_CREDUSAGE_DEFAULT_TLS`: defaults for TLS certificates, and/or
+// - `CUPS_CREDUSAGE_ALL`: all usages.
+//
+// The "organization", "org_unit", "locality", "state_province", and "country"
+// arguments specify information about the identity and geolocation of the
+// issuer.
+//
+// The "common_name" argument specifies the common name and the "num_alt_names"
+// and "alt_names" arguments specify a list of DNS hostnames for the
+// certificate.
+//
-int /* O - 1 on success, 0 on failure */
-cupsSetServerCredentials(
- const char *path, /* I - Path to keychain/directory */
- const char *common_name, /* I - Default common name for server */
- int auto_create) /* I - 1 = automatically create self-signed certificates */
+bool // O - `true` on success, `false` on error
+cupsCreateCredentialsRequest(
+ const char *path, // I - Directory path for certificate/key store or `NULL` for default
+ cups_credpurpose_t purpose, // I - Credential purposes
+ cups_credtype_t type, // I - Credential type
+ cups_credusage_t usage, // I - Credential usages
+ const char *organization, // I - Organization or `NULL` to use common name
+ const char *org_unit, // I - Organizational unit or `NULL` for none
+ const char *locality, // I - City/town or `NULL` for "Unknown"
+ const char *state_province, // I - State/province or `NULL` for "Unknown"
+ const char *country, // I - Country or `NULL` for locale-based default
+ const char *common_name, // I - Common name
+ size_t num_alt_names, // I - Number of subject alternate names
+ const char * const *alt_names) // I - Subject Alternate Names
{
- char temp[1024]; /* Default path buffer */
+ bool ret = false; // Return value
+ gnutls_x509_crq_t crq = NULL; // Certificate request
+ gnutls_x509_privkey_t key = NULL; // Private/public key pair
+ char temp[1024], // Temporary directory name
+ csrfile[1024], // Certificate signing request filename
+ keyfile[1024]; // Private key filename
+ unsigned gnutls_usage = 0;// GNU TLS keyUsage bits
+ cups_file_t *fp; // Key/cert file
+ unsigned char buffer[8192]; // Buffer for key/cert data
+ size_t bytes; // Number of bytes of data
+ int err; // GNU TLS status
+
+
+ DEBUG_printf(("cupsCreateCredentialsRequest(path=\"%s\", purpose=0x%x, type=%d, usage=0x%x, organization=\"%s\", org_unit=\"%s\", locality=\"%s\", state_province=\"%s\", country=\"%s\", common_name=\"%s\", num_alt_names=%u, alt_names=%p)", path, purpose, type, usage, organization, org_unit, locality, state_province, country, common_name, (unsigned)num_alt_names, alt_names));
+
+ // Filenames...
+ if (!path)
+ path = http_default_path(temp, sizeof(temp));
+
+ if (!path || !common_name)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ goto done;
+ }
+ http_make_path(csrfile, sizeof(csrfile), path, common_name, "csr");
+ http_make_path(keyfile, sizeof(keyfile), path, common_name, "key");
- DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
+ // Create the encryption key...
+ DEBUG_puts("1cupsCreateCredentialsRequest: Creating key pair.");
- /*
- * Use defaults as needed...
- */
+ key = gnutls_create_key(type);
- if (!path)
- path = http_gnutls_default_path(temp, sizeof(temp));
+ DEBUG_puts("1cupsCreateCredentialsRequest: Key pair created.");
- /*
- * Range check input...
- */
+ // Save it...
+ bytes = sizeof(buffer);
- if (!path || !common_name)
+ if ((err = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0)
+ {
+ DEBUG_printf(("1cupsCreateCredentialsRequest: Unable to export private key: %s", gnutls_strerror(err)));
+ goto done;
+ }
+ else if ((fp = cupsFileOpen(keyfile, "w")) != NULL)
+ {
+ DEBUG_printf(("1cupsCreateCredentialsRequest: Writing private key to \"%s\".", keyfile));
+ cupsFileWrite(fp, (char *)buffer, bytes);
+ cupsFileClose(fp);
+ }
+ else
+ {
+ DEBUG_printf(("1cupsCreateCredentialsRequest: Unable to create private key file \"%s\": %s", keyfile, strerror(errno)));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ goto done;
+ }
+
+ // Create the certificate...
+ DEBUG_puts("1cupsCreateCredentialsRequest: Generating X.509 certificate request.");
+
+ if (!organization)
+ organization = common_name;
+ if (!org_unit)
+ org_unit = "";
+ if (!locality)
+ locality = "Unknown";
+ if (!state_province)
+ state_province = "Unknown";
+ if (!country)
+ country = "US";
+
+ gnutls_x509_crq_init(&crq);
+ gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_COUNTRY_NAME, 0, country, (unsigned)strlen(country));
+ gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_COMMON_NAME, 0, common_name, (unsigned)strlen(common_name));
+ gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, organization, (unsigned)strlen(organization));
+ gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, org_unit, (unsigned)strlen(org_unit));
+ gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, state_province, (unsigned)strlen(state_province));
+ gnutls_x509_crq_set_dn_by_oid(crq, GNUTLS_OID_X520_LOCALITY_NAME, 0, locality, (unsigned)strlen(locality));
+ gnutls_x509_crq_set_key(crq, key);
+ gnutls_x509_crq_set_subject_alt_name(crq, GNUTLS_SAN_DNSNAME, common_name, (unsigned)strlen(common_name), GNUTLS_FSAN_SET);
+ if (!strchr(common_name, '.'))
+ {
+ // Add common_name.local to the list, too...
+ char localname[256]; // hostname.local
+
+ snprintf(localname, sizeof(localname), "%s.local", common_name);
+ gnutls_x509_crq_set_subject_alt_name(crq, GNUTLS_SAN_DNSNAME, localname, (unsigned)strlen(localname), GNUTLS_FSAN_APPEND);
+ }
+ gnutls_x509_crq_set_subject_alt_name(crq, GNUTLS_SAN_DNSNAME, "localhost", 9, GNUTLS_FSAN_APPEND);
+ if (num_alt_names > 0)
+ {
+ size_t i; // Looping var
+
+ for (i = 0; i < num_alt_names; i ++)
+ {
+ if (strcmp(alt_names[i], "localhost"))
+ {
+ gnutls_x509_crq_set_subject_alt_name(crq, GNUTLS_SAN_DNSNAME, alt_names[i], (unsigned)strlen(alt_names[i]), GNUTLS_FSAN_APPEND);
+ }
+ }
+ }
+
+ if (purpose & CUPS_CREDPURPOSE_SERVER_AUTH)
+ gnutls_x509_crq_set_key_purpose_oid(crq, GNUTLS_KP_TLS_WWW_SERVER, 0);
+ if (purpose & CUPS_CREDPURPOSE_CLIENT_AUTH)
+ gnutls_x509_crq_set_key_purpose_oid(crq, GNUTLS_KP_TLS_WWW_CLIENT, 0);
+ if (purpose & CUPS_CREDPURPOSE_CODE_SIGNING)
+ gnutls_x509_crq_set_key_purpose_oid(crq, GNUTLS_KP_CODE_SIGNING, 0);
+ if (purpose & CUPS_CREDPURPOSE_EMAIL_PROTECTION)
+ gnutls_x509_crq_set_key_purpose_oid(crq, GNUTLS_KP_EMAIL_PROTECTION, 0);
+ if (purpose & CUPS_CREDPURPOSE_OCSP_SIGNING)
+ gnutls_x509_crq_set_key_purpose_oid(crq, GNUTLS_KP_OCSP_SIGNING, 0);
+
+ if (usage & CUPS_CREDUSAGE_DIGITAL_SIGNATURE)
+ gnutls_usage |= GNUTLS_KEY_DIGITAL_SIGNATURE;
+ if (usage & CUPS_CREDUSAGE_NON_REPUDIATION)
+ gnutls_usage |= GNUTLS_KEY_NON_REPUDIATION;
+ if (usage & CUPS_CREDUSAGE_KEY_ENCIPHERMENT)
+ gnutls_usage |= GNUTLS_KEY_KEY_ENCIPHERMENT;
+ if (usage & CUPS_CREDUSAGE_DATA_ENCIPHERMENT)
+ gnutls_usage |= GNUTLS_KEY_DATA_ENCIPHERMENT;
+ if (usage & CUPS_CREDUSAGE_KEY_AGREEMENT)
+ gnutls_usage |= GNUTLS_KEY_KEY_AGREEMENT;
+ if (usage & CUPS_CREDUSAGE_KEY_CERT_SIGN)
+ gnutls_usage |= GNUTLS_KEY_KEY_CERT_SIGN;
+ if (usage & CUPS_CREDUSAGE_CRL_SIGN)
+ gnutls_usage |= GNUTLS_KEY_CRL_SIGN;
+ if (usage & CUPS_CREDUSAGE_ENCIPHER_ONLY)
+ gnutls_usage |= GNUTLS_KEY_ENCIPHER_ONLY;
+ if (usage & CUPS_CREDUSAGE_DECIPHER_ONLY)
+ gnutls_usage |= GNUTLS_KEY_DECIPHER_ONLY;
+
+ gnutls_x509_crq_set_key_usage(crq, gnutls_usage);
+ gnutls_x509_crq_set_version(crq, 3);
+
+ gnutls_x509_crq_sign2(crq, key, GNUTLS_DIG_SHA256, 0);
+
+ // Save it...
+ bytes = sizeof(buffer);
+ if ((err = gnutls_x509_crq_export(crq, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0)
+ {
+ DEBUG_printf(("1cupsCreateCredentialsRequest: Unable to export public key and X.509 certificate request: %s", gnutls_strerror(err)));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(err), 0);
+ goto done;
+ }
+ else if ((fp = cupsFileOpen(csrfile, "w")) != NULL)
+ {
+ DEBUG_printf(("1cupsCreateCredentialsRequest: Writing public key and X.509 certificate request to \"%s\".", csrfile));
+ cupsFileWrite(fp, (char *)buffer, bytes);
+ cupsFileClose(fp);
+ }
+ else
+ {
+ DEBUG_printf(("1cupsCreateCredentialsRequest: Unable to create public key and X.509 certificate request file \"%s\": %s", csrfile, strerror(errno)));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ goto done;
+ }
+
+ DEBUG_puts("1cupsCreateCredentialsRequest: Successfully created credentials request.");
+
+ ret = true;
+
+ // Cleanup...
+ done:
+
+ if (crq)
+ gnutls_x509_crq_deinit(crq);
+ if (key)
+ gnutls_x509_privkey_deinit(key);
+
+ return (ret);
+}
+
+
+//
+// 'cupsSignCredentialsRequest()' - Sign an X.509 certificate signing request to produce an X.509 certificate chain.
+//
+// This function creates an X.509 certificate from a signing request. The
+// certificate is stored in the directory "path" or, if "path" is `NULL`, in a
+// per-user or system-wide (when running as root) certificate/key store. The
+// generated certificate is signed by the named root certificate or, if
+// "root_name" is `NULL`, a site-wide default root certificate. When
+// "root_name" is `NULL` and there is no site-wide default root certificate, a
+// self-signed certificate is generated instead.
+//
+// The "allowed_purpose" argument specifies the allowed purpose(s) used for the
+// credentials as a bitwise OR of the following constants:
+//
+// - `CUPS_CREDPURPOSE_SERVER_AUTH` for validating TLS servers,
+// - `CUPS_CREDPURPOSE_CLIENT_AUTH` for validating TLS clients,
+// - `CUPS_CREDPURPOSE_CODE_SIGNING` for validating compiled code,
+// - `CUPS_CREDPURPOSE_EMAIL_PROTECTION` for validating email messages,
+// - `CUPS_CREDPURPOSE_TIME_STAMPING` for signing timestamps to objects, and/or
+// - `CUPS_CREDPURPOSE_OCSP_SIGNING` for Online Certificate Status Protocol
+// message signing.
+//
+// The "allowed_usage" argument specifies the allowed usage(s) for the
+// credentials as a bitwise OR of the following constants:
+//
+// - `CUPS_CREDUSAGE_DIGITAL_SIGNATURE`: digital signatures,
+// - `CUPS_CREDUSAGE_NON_REPUDIATION`: non-repudiation/content commitment,
+// - `CUPS_CREDUSAGE_KEY_ENCIPHERMENT`: key encipherment,
+// - `CUPS_CREDUSAGE_DATA_ENCIPHERMENT`: data encipherment,
+// - `CUPS_CREDUSAGE_KEY_AGREEMENT`: key agreement,
+// - `CUPS_CREDUSAGE_KEY_CERT_SIGN`: key certicate signing,
+// - `CUPS_CREDUSAGE_CRL_SIGN`: certificate revocation list signing,
+// - `CUPS_CREDUSAGE_ENCIPHER_ONLY`: encipherment only,
+// - `CUPS_CREDUSAGE_DECIPHER_ONLY`: decipherment only,
+// - `CUPS_CREDUSAGE_DEFAULT_CA`: defaults for CA certificates,
+// - `CUPS_CREDUSAGE_DEFAULT_TLS`: defaults for TLS certificates, and/or
+// - `CUPS_CREDUSAGE_ALL`: all usages.
+//
+// The "cb" and "cb_data" arguments specify a function and its data that are
+// used to validate any subjectAltName values in the signing request:
+//
+// ```
+// bool san_cb(const char *common_name, const char *alt_name, void *cb_data) {
+// ... return true if OK and false if not ...
+// }
+// ```
+//
+// If `NULL`, a default validation function is used that allows "localhost" and
+// variations of the common name.
+//
+// The "expiration_date" argument specifies the expiration date and time as a
+// Unix `time_t` value in seconds.
+//
+
+bool // O - `true` on success, `false` on failure
+cupsSignCredentialsRequest(
+ const char *path, // I - Directory path for certificate/key store or `NULL` for default
+ const char *common_name, // I - Common name to use
+ const char *request, // I - PEM-encoded CSR
+ const char *root_name, // I - Root certificate
+ cups_credpurpose_t allowed_purpose, // I - Allowed credential purpose(s)
+ cups_credusage_t allowed_usage, // I - Allowed credential usage(s)
+ cups_cert_san_cb_t cb, // I - subjectAltName callback or `NULL` to allow just .local
+ void *cb_data, // I - Callback data
+ time_t expiration_date) // I - Certificate expiration date
+{
+ bool ret = false; // Return value
+ int i, // Looping var
+ err; // GNU TLS error code, if any
+ gnutls_x509_crq_t crq = NULL; // Certificate request
+ gnutls_x509_crt_t crt = NULL; // Certificate
+ gnutls_x509_crt_t root_crt = NULL;// Root certificate
+ gnutls_x509_privkey_t root_key = NULL;// Root private key
+ gnutls_datum_t datum; // Datum
+ char defpath[1024], // Default path
+ temp[1024], // Temporary string
+ crtfile[1024], // Certificate filename
+ *root_crtdata, // Root certificate data
+ *root_keydata; // Root private key data
+ size_t tempsize; // Size of temporary string
+ cups_credpurpose_t purpose; // Credential purpose(s)
+ unsigned gnutls_usage; // GNU TLS keyUsage bits
+ cups_credusage_t usage; // Credential usage(s)
+ cups_file_t *fp; // Key/cert file
+ unsigned char buffer[65536]; // Buffer for x509 data
+ size_t bytes; // Number of bytes of data
+ unsigned char serial[4]; // Serial number buffer
+ time_t curtime; // Current time
+
+
+ DEBUG_printf(("cupsSignCredentialsRequest(path=\"%s\", common_name=\"%s\", request=\"%s\", root_name=\"%s\", allowed_purpose=0x%x, allowed_usage=0x%x, cb=%p, cb_data=%p, expiration_date=%ld)", path, common_name, request, root_name, allowed_purpose, allowed_usage, cb, cb_data, (long)expiration_date));
+
+ // Filenames...
+ if (!path)
+ path = http_default_path(defpath, sizeof(defpath));
+
+ if (!path || !common_name || !request)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
- return (0);
+ goto done;
}
- cupsMutexLock(&tls_mutex);
+ if (!cb)
+ cb = http_default_san_cb;
+
+ // Import the request...
+ gnutls_x509_crq_init(&crq);
- /*
- * Free old values...
- */
+ datum.data = (unsigned char *)request;
+ datum.size = strlen(request);
- if (tls_keypath)
- _cupsStrFree(tls_keypath);
+ if ((err = gnutls_x509_crq_import(crq, &datum, GNUTLS_X509_FMT_PEM)) < 0)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(err), 0);
+ goto done;
+ }
- if (tls_common_name)
- _cupsStrFree(tls_common_name);
+ // Create the certificate...
+ DEBUG_puts("1cupsSignCredentialsRequest: Generating X.509 certificate.");
- /*
- * Save the new values...
- */
+ curtime = time(NULL);
+ serial[0] = (unsigned char)(curtime >> 24);
+ serial[1] = (unsigned char)(curtime >> 16);
+ serial[2] = (unsigned char)(curtime >> 8);
+ serial[3] = (unsigned char)(curtime);
- tls_keypath = _cupsStrAlloc(path);
- tls_auto_create = auto_create;
- tls_common_name = _cupsStrAlloc(common_name);
+ gnutls_x509_crt_init(&crt);
+ gnutls_x509_crt_set_crq(crt, crq);
- cupsMutexUnlock(&tls_mutex);
+#if 0
+ tempsize = sizeof(temp) - 1;
+ if (gnutls_x509_crq_get_dn_by_oid(crq, GNUTLS_OID_X520_COUNTRY_NAME, 0, 0, temp, &tempsize) >= 0)
+ temp[tempsize] = '\0';
+ else
+ cupsCopyString(temp, "US", sizeof(temp));
+ gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COUNTRY_NAME, 0, temp, (unsigned)strlen(temp));
+
+ gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_COMMON_NAME, 0, common_name, strlen(common_name));
+
+ tempsize = sizeof(temp) - 1;
+ if (gnutls_x509_crq_get_dn_by_oid(crq, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, 0, temp, &tempsize) >= 0)
+ {
+ temp[tempsize] = '\0';
+ gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATION_NAME, 0, temp, (unsigned)strlen(temp));
+ }
+
+ tempsize = sizeof(temp) - 1;
+ if (gnutls_x509_crq_get_dn_by_oid(crq, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, 0, temp, &tempsize) >= 0)
+ {
+ temp[tempsize] = '\0';
+ gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_ORGANIZATIONAL_UNIT_NAME, 0, temp, (unsigned)strlen(temp));
+ }
+
+ tempsize = sizeof(temp) - 1;
+ if (gnutls_x509_crq_get_dn_by_oid(crq, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, 0, temp, &tempsize) >= 0)
+ temp[tempsize] = '\0';
+ else
+ cupsCopyString(temp, "Unknown", sizeof(temp));
+ gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_STATE_OR_PROVINCE_NAME, 0, temp, strlen(temp));
+
+ tempsize = sizeof(temp) - 1;
+ if (gnutls_x509_crq_get_dn_by_oid(crq, GNUTLS_OID_X520_LOCALITY_NAME, 0, 0, temp, &tempsize) >= 0)
+ temp[tempsize] = '\0';
+ else
+ cupsCopyString(temp, "Unknown", sizeof(temp));
+ gnutls_x509_crt_set_dn_by_oid(crt, GNUTLS_OID_X520_LOCALITY_NAME, 0, temp, strlen(temp));
+#endif // 0
+
+ gnutls_x509_crt_set_serial(crt, serial, sizeof(serial));
+ gnutls_x509_crt_set_activation_time(crt, curtime);
+ gnutls_x509_crt_set_expiration_time(crt, expiration_date);
+ gnutls_x509_crt_set_ca_status(crt, 0);
+
+ for (i = 0; i < 100; i ++)
+ {
+ unsigned type; // Name type
+
+ tempsize = sizeof(temp) - 1;
+ if (gnutls_x509_crq_get_subject_alt_name(crq, i, temp, &tempsize, &type, NULL) < 0)
+ break;
+
+ temp[tempsize] = '\0';
+
+ DEBUG_printf(("1cupsSignCredentialsRequest: SAN %s", temp));
+
+ if (type != GNUTLS_SAN_DNSNAME || (cb)(common_name, temp, cb_data))
+ {
+ // Good subjectAltName
+// gnutls_x509_crt_set_subject_alt_name(crt, type, temp, (unsigned)strlen(temp), i ? GNUTLS_FSAN_APPEND : GNUTLS_FSAN_SET);
+ }
+ else
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Validation of subjectAltName in X.509 certificate request failed."), 1);
+ goto done;
+ }
+ }
+
+ for (purpose = 0, i = 0; i < 100; i ++)
+ {
+ tempsize = sizeof(temp) - 1;
+ if (gnutls_x509_crq_get_key_purpose_oid(crq, i, temp, &tempsize, NULL) < 0)
+ break;
+ temp[tempsize] = '\0';
+
+ if (!strcmp(temp, GNUTLS_KP_TLS_WWW_SERVER))
+ purpose |= CUPS_CREDPURPOSE_SERVER_AUTH;
+ if (!strcmp(temp, GNUTLS_KP_TLS_WWW_CLIENT))
+ purpose |= CUPS_CREDPURPOSE_CLIENT_AUTH;
+ if (!strcmp(temp, GNUTLS_KP_CODE_SIGNING))
+ purpose |= CUPS_CREDPURPOSE_CODE_SIGNING;
+ if (!strcmp(temp, GNUTLS_KP_EMAIL_PROTECTION))
+ purpose |= CUPS_CREDPURPOSE_EMAIL_PROTECTION;
+ if (!strcmp(temp, GNUTLS_KP_OCSP_SIGNING))
+ purpose |= CUPS_CREDPURPOSE_OCSP_SIGNING;
+ }
+ DEBUG_printf(("1cupsSignCredentialsRequest: purpose=0x%04x", purpose));
+
+ if (purpose & ~allowed_purpose)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad keyUsage extension in X.509 certificate request."), 1);
+ goto done;
+ }
+
+#if 0
+ if (purpose == 0 || (purpose & CUPS_CREDPURPOSE_SERVER_AUTH))
+ gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_SERVER, 0);
+ if (purpose & CUPS_CREDPURPOSE_CLIENT_AUTH)
+ gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_TLS_WWW_CLIENT, 0);
+ if (purpose & CUPS_CREDPURPOSE_CODE_SIGNING)
+ gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_CODE_SIGNING, 0);
+ if (purpose & CUPS_CREDPURPOSE_EMAIL_PROTECTION)
+ gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_EMAIL_PROTECTION, 0);
+ if (purpose & CUPS_CREDPURPOSE_OCSP_SIGNING)
+ gnutls_x509_crt_set_key_purpose_oid(crt, GNUTLS_KP_OCSP_SIGNING, 0);
+#endif // 0
+
+ if (gnutls_x509_crq_get_key_usage(crq, &gnutls_usage, NULL) < 0)
+ {
+ // No keyUsage, use default for TLS...
+ gnutls_usage = GNUTLS_KEY_DIGITAL_SIGNATURE | GNUTLS_KEY_KEY_ENCIPHERMENT;
+ }
+ else
+ {
+ // Got keyUsage, convert to CUPS bitfield
+ usage = 0;
+ if (gnutls_usage & GNUTLS_KEY_DIGITAL_SIGNATURE)
+ usage |= CUPS_CREDUSAGE_DIGITAL_SIGNATURE;
+ if (gnutls_usage & GNUTLS_KEY_NON_REPUDIATION)
+ usage |= CUPS_CREDUSAGE_NON_REPUDIATION;
+ if (gnutls_usage & GNUTLS_KEY_KEY_ENCIPHERMENT)
+ usage |= CUPS_CREDUSAGE_KEY_ENCIPHERMENT;
+ if (gnutls_usage & GNUTLS_KEY_DATA_ENCIPHERMENT)
+ usage |= CUPS_CREDUSAGE_DATA_ENCIPHERMENT;
+ if (gnutls_usage & GNUTLS_KEY_KEY_AGREEMENT)
+ usage |= CUPS_CREDUSAGE_KEY_AGREEMENT;
+ if (gnutls_usage & GNUTLS_KEY_KEY_CERT_SIGN)
+ usage |= CUPS_CREDUSAGE_KEY_CERT_SIGN;
+ if (gnutls_usage & GNUTLS_KEY_CRL_SIGN)
+ usage |= CUPS_CREDUSAGE_CRL_SIGN;
+ if (gnutls_usage & GNUTLS_KEY_ENCIPHER_ONLY)
+ usage |= CUPS_CREDUSAGE_ENCIPHER_ONLY;
+ if (gnutls_usage & GNUTLS_KEY_DECIPHER_ONLY)
+ usage |= CUPS_CREDUSAGE_DECIPHER_ONLY;
+
+ DEBUG_printf(("1cupsSignCredentialsRequest: usage=0x%04x", usage));
+
+ if (usage & ~allowed_usage)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad extKeyUsage extension in X.509 certificate request."), 1);
+ goto done;
+ }
+ }
+// gnutls_x509_crt_set_key_usage(crt, gnutls_usage);
+
+ gnutls_x509_crt_set_version(crt, 3);
+
+ bytes = sizeof(buffer);
+ if (gnutls_x509_crt_get_key_id(crt, 0, buffer, &bytes) >= 0)
+ gnutls_x509_crt_set_subject_key_id(crt, buffer, bytes);
+
+ // Try loading a root certificate...
+ root_crtdata = cupsCopyCredentials(path, root_name ? root_name : "_site_");
+ root_keydata = cupsCopyCredentialsKey(path, root_name ? root_name : "_site_");
+
+ if (root_crtdata && root_keydata)
+ {
+ // Load root certificate...
+ datum.data = (unsigned char *)root_crtdata;
+ datum.size = strlen(root_crtdata);
+
+ gnutls_x509_crt_init(&root_crt);
+ if (gnutls_x509_crt_import(root_crt, &datum, GNUTLS_X509_FMT_PEM) < 0)
+ {
+ // No good, clear it...
+ gnutls_x509_crt_deinit(root_crt);
+ root_crt = NULL;
+ }
+ else
+ {
+ // Load root private key...
+ datum.data = (unsigned char *)root_keydata;
+ datum.size = strlen(root_keydata);
+
+ gnutls_x509_privkey_init(&root_key);
+ if (gnutls_x509_privkey_import(root_key, &datum, GNUTLS_X509_FMT_PEM) < 0)
+ {
+ // No food, clear them...
+ gnutls_x509_privkey_deinit(root_key);
+ root_key = NULL;
- return (1);
+ gnutls_x509_crt_deinit(root_crt);
+ root_crt = NULL;
+ }
+ }
+ }
+
+ free(root_crtdata);
+ free(root_keydata);
+
+ if (!root_crt || !root_key)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to load X.509 CA certificate and private key."), 1);
+ goto done;
+ }
+
+ gnutls_x509_crt_sign(crt, root_crt, root_key);
+
+ // Save it...
+ http_make_path(crtfile, sizeof(crtfile), path, common_name, "crt");
+
+ bytes = sizeof(buffer);
+ if ((err = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, buffer, &bytes)) < 0)
+ {
+ DEBUG_printf(("1cupsSignCredentialsRequest: Unable to export public key and X.509 certificate: %s", gnutls_strerror(err)));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, gnutls_strerror(err), 0);
+ goto done;
+ }
+ else if ((fp = cupsFileOpen(crtfile, "w")) != NULL)
+ {
+ DEBUG_printf(("1cupsSignCredentialsRequest: Writing public key and X.509 certificate to \"%s\".", crtfile));
+ cupsFileWrite(fp, (char *)buffer, bytes);
+ cupsFileClose(fp);
+ }
+ else
+ {
+ DEBUG_printf(("1cupsSignCredentialsRequest: Unable to create public key and X.509 certificate file \"%s\": %s", crtfile, strerror(errno)));
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ goto done;
+ }
+
+ DEBUG_puts("1cupsSignCredentialsRequest: Successfully created credentials.");
+
+ ret = true;
+
+ // Cleanup...
+ done:
+
+ if (crq)
+ gnutls_x509_crq_deinit(crq);
+ if (crt)
+ gnutls_x509_crt_deinit(crt);
+ if (root_crt)
+ gnutls_x509_crt_deinit(root_crt);
+ if (root_key)
+ gnutls_x509_privkey_deinit(root_key);
+
+ return (ret);
}
-/*
- * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
- * an encrypted connection.
- */
+//
+// 'httpCopyCredentials()' - Copy the credentials associated with the peer in
+// an encrypted connection.
+//
-int /* O - Status of call (0 = success) */
+bool // O - `true` on success, `false` on failure
httpCopyCredentials(
- http_t *http, /* I - Connection to server */
- cups_array_t **credentials) /* O - Array of credentials */
+ http_t *http, // I - Connection to server
+ cups_array_t **credentials) // O - Array of credentials
{
- unsigned count; /* Number of certificates */
- const gnutls_datum_t *certs; /* Certificates */
+ unsigned count; // Number of certificates
+ const gnutls_datum_t *certs; // Certificates
DEBUG_printf(("httpCopyCredentials(http=%p, credentials=%p)", http, credentials));
@@ -318,7 +991,7 @@ httpCopyCredentials(
*credentials = NULL;
if (!http || !http->tls || !credentials)
- return (-1);
+ return (false);
*credentials = cupsArrayNew(NULL, NULL, NULL, 0, NULL, NULL);
certs = gnutls_certificate_get_peers(http->tls, &count);
@@ -335,17 +1008,17 @@ httpCopyCredentials(
}
}
- return (0);
+ return (true);
}
-/*
- * '_httpCreateCredentials()' - Create credentials in the internal format.
- */
+//
+// '_httpCreateCredentials()' - Create credentials in the internal format.
+//
-http_tls_credentials_t /* O - Internal credentials */
+http_tls_credentials_t // O - Internal credentials
_httpCreateCredentials(
- cups_array_t *credentials) /* I - Array of credentials */
+ cups_array_t *credentials) // I - Array of credentials
{
(void)credentials;
@@ -353,32 +1026,20 @@ _httpCreateCredentials(
}
-/*
- * '_httpFreeCredentials()' - Free internal credentials.
- */
-
-void
-_httpFreeCredentials(
- http_tls_credentials_t credentials) /* I - Internal credentials */
-{
- (void)credentials;
-}
-
-
-/*
- * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
- */
+//
+// 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
+//
-int /* O - 1 if valid, 0 otherwise */
+bool // O - `true` if valid, `false` otherwise
httpCredentialsAreValidForName(
- cups_array_t *credentials, /* I - Credentials */
- const char *common_name) /* I - Name to check */
+ cups_array_t *credentials, // I - Credentials
+ const char *common_name) // I - Name to check
{
- gnutls_x509_crt_t cert; /* Certificate */
- int result = 0; /* Result */
+ gnutls_x509_crt_t cert; // Certificate
+ bool result = false; // Result
- cert = http_gnutls_create_credential((http_credential_t *)cupsArrayGetFirst(credentials));
+ cert = gnutls_create_credential((http_credential_t *)cupsArrayGetFirst(credentials));
if (cert)
{
result = gnutls_x509_crt_check_hostname(cert, common_name) != 0;
@@ -386,11 +1047,11 @@ httpCredentialsAreValidForName(
if (result)
{
gnutls_x509_crl_iter_t iter = NULL;
- /* Iterator */
- unsigned char cserial[1024], /* Certificate serial number */
- rserial[1024]; /* Revoked serial number */
- size_t cserial_size, /* Size of cert serial number */
- rserial_size; /* Size of revoked serial number */
+ // Iterator
+ unsigned char cserial[1024], // Certificate serial number
+ rserial[1024]; // Revoked serial number
+ size_t cserial_size, // Size of cert serial number
+ rserial_size; // Size of revoked serial number
cupsMutexLock(&tls_mutex);
@@ -405,7 +1066,7 @@ httpCredentialsAreValidForName(
{
if (cserial_size == rserial_size && !memcmp(cserial, rserial, rserial_size))
{
- result = 0;
+ result = false;
break;
}
@@ -424,21 +1085,21 @@ httpCredentialsAreValidForName(
}
-/*
- * 'httpCredentialsGetTrust()' - Return the trust of credentials.
- */
+//
+// 'httpCredentialsGetTrust()' - Return the trust of credentials.
+//
-http_trust_t /* O - Level of trust */
+http_trust_t // O - Level of trust
httpCredentialsGetTrust(
- cups_array_t *credentials, /* I - Credentials */
- const char *common_name) /* I - Common name for trust lookup */
+ cups_array_t *credentials, // I - Credentials
+ const char *common_name) // I - Common name for trust lookup
{
http_trust_t trust = HTTP_TRUST_OK;
- /* Trusted? */
- gnutls_x509_crt_t cert; /* Certificate */
- cups_array_t *tcreds = NULL; /* Trusted credentials */
+ // Trusted?
+ gnutls_x509_crt_t cert; // Certificate
+ cups_array_t *tcreds = NULL; // Trusted credentials
_cups_globals_t *cg = _cupsGlobals();
- /* Per-thread globals */
+ // Per-thread globals
if (!common_name)
@@ -447,7 +1108,7 @@ httpCredentialsGetTrust(
return (HTTP_TRUST_UNKNOWN);
}
- if ((cert = http_gnutls_create_credential((http_credential_t *)cupsArrayGetFirst(credentials))) == NULL)
+ if ((cert = gnutls_create_credential((http_credential_t *)cupsArrayGetFirst(credentials))) == NULL)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create credentials from array."), 1);
return (HTTP_TRUST_UNKNOWN);
@@ -456,66 +1117,48 @@ httpCredentialsGetTrust(
if (cg->any_root < 0)
{
_cupsSetDefaults();
- http_gnutls_load_crl();
+ gnutls_load_crl();
}
- /*
- * Look this common name up in the default keychains...
- */
-
+ // Look this common name up in the default keychains...
httpLoadCredentials(NULL, &tcreds, common_name);
if (tcreds)
{
- char credentials_str[1024], /* String for incoming credentials */
- tcreds_str[1024]; /* String for saved credentials */
+ char credentials_str[1024], // String for incoming credentials
+ tcreds_str[1024]; // String for saved credentials
httpCredentialsString(credentials, credentials_str, sizeof(credentials_str));
httpCredentialsString(tcreds, tcreds_str, sizeof(tcreds_str));
if (strcmp(credentials_str, tcreds_str))
{
- /*
- * Credentials don't match, let's look at the expiration date of the new
- * credentials and allow if the new ones have a later expiration...
- */
-
+ // Credentials don't match, let's look at the expiration date of the new
+ // credentials and allow if the new ones have a later expiration...
if (!cg->trust_first)
{
- /*
- * Do not trust certificates on first use...
- */
-
+ // Do not trust certificates on first use...
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Trust on first use is disabled."), 1);
trust = HTTP_TRUST_INVALID;
}
else if (httpCredentialsGetExpiration(credentials) <= httpCredentialsGetExpiration(tcreds))
{
- /*
- * The new credentials are not newly issued...
- */
-
+ // The new credentials are not newly issued...
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("New credentials are older than stored credentials."), 1);
trust = HTTP_TRUST_INVALID;
}
else if (!httpCredentialsAreValidForName(credentials, common_name))
{
- /*
- * The common name does not match the issued certificate...
- */
-
+ // The common name does not match the issued certificate...
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("New credentials are not valid for name."), 1);
trust = HTTP_TRUST_INVALID;
}
else if (httpCredentialsGetExpiration(tcreds) < time(NULL))
{
- /*
- * Save the renewed credentials...
- */
-
+ // Save the renewed credentials...
trust = HTTP_TRUST_RENEWED;
httpSaveCredentials(NULL, credentials, common_name);
@@ -531,33 +1174,24 @@ httpCredentialsGetTrust(
}
else if (!cg->trust_first)
{
- /*
- * See if we have a site CA certificate we can compare...
- */
-
+ // See if we have a site CA certificate we can compare...
if (!httpLoadCredentials(NULL, &tcreds, "site"))
{
if (cupsArrayGetCount(credentials) != (cupsArrayGetCount(tcreds) + 1))
{
- /*
- * Certificate isn't directly generated from the CA cert...
- */
-
+ // Certificate isn't directly generated from the CA cert...
trust = HTTP_TRUST_INVALID;
}
else
{
- /*
- * Do a tail comparison of the two certificates...
- */
-
- http_credential_t *a, *b; /* Certificates */
+ // Do a tail comparison of the two certificates...
+ http_credential_t *a, *b; // Certificates
- for (a = (http_credential_t *)cupsArrayGetFirst(tcreds), b = (http_credential_t *)cupsArrayGetElement(credentials, 1);
- a && b;
- a = (http_credential_t *)cupsArrayGetNext(tcreds), b = (http_credential_t *)cupsArrayGetNext(credentials))
+ for (a = (http_credential_t *)cupsArrayGetFirst(tcreds), b = (http_credential_t *)cupsArrayGetElement(credentials, 1); a && b; a = (http_credential_t *)cupsArrayGetNext(tcreds), b = (http_credential_t *)cupsArrayGetNext(credentials))
+ {
if (a->datalen != b->datalen || memcmp(a->data, b->data, a->datalen))
break;
+ }
if (a || b)
trust = HTTP_TRUST_INVALID;
@@ -575,7 +1209,7 @@ httpCredentialsGetTrust(
if (trust == HTTP_TRUST_OK && !cg->expired_certs)
{
- time_t curtime; /* Current date/time */
+ time_t curtime; // Current date/time
time(&curtime);
if (curtime < gnutls_x509_crt_get_activation_time(cert) ||
@@ -598,19 +1232,19 @@ httpCredentialsGetTrust(
}
-/*
- * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
- */
+//
+// 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
+//
-time_t /* O - Expiration date of credentials */
+time_t // O - Expiration date of credentials
httpCredentialsGetExpiration(
- cups_array_t *credentials) /* I - Credentials */
+ cups_array_t *credentials) // I - Credentials
{
- gnutls_x509_crt_t cert; /* Certificate */
- time_t result = 0; /* Result */
+ gnutls_x509_crt_t cert; // Certificate
+ time_t result = 0; // Result
- cert = http_gnutls_create_credential((http_credential_t *)cupsArrayGetFirst(credentials));
+ cert = gnutls_create_credential((http_credential_t *)cupsArrayGetFirst(credentials));
if (cert)
{
result = gnutls_x509_crt_get_expiration_time(cert);
@@ -621,18 +1255,18 @@ httpCredentialsGetExpiration(
}
-/*
- * 'httpCredentialsString()' - Return a string representing the credentials.
- */
+//
+// 'httpCredentialsString()' - Return a string representing the credentials.
+//
-size_t /* O - Total size of credentials string */
+size_t // O - Total size of credentials string
httpCredentialsString(
- cups_array_t *credentials, /* I - Credentials */
- char *buffer, /* I - Buffer or @code NULL@ */
- size_t bufsize) /* I - Size of buffer */
+ cups_array_t *credentials, // I - Credentials
+ char *buffer, // I - Buffer or @code NULL@
+ size_t bufsize) // I - Size of buffer
{
- http_credential_t *first; /* First certificate */
- gnutls_x509_crt_t cert; /* Certificate */
+ http_credential_t *first; // First certificate
+ gnutls_x509_crt_t cert; // Certificate
DEBUG_printf(("httpCredentialsString(credentials=%p, buffer=%p, bufsize=" CUPS_LLFMT ")", credentials, buffer, CUPS_LLCAST bufsize));
@@ -644,15 +1278,15 @@ httpCredentialsString(
*buffer = '\0';
if ((first = (http_credential_t *)cupsArrayGetFirst(credentials)) != NULL &&
- (cert = http_gnutls_create_credential(first)) != NULL)
+ (cert = gnutls_create_credential(first)) != NULL)
{
- char name[256], /* Common name associated with cert */
- issuer[256]; /* Issuer associated with cert */
- size_t len; /* Length of string */
- time_t expiration; /* Expiration date of cert */
+ char name[256], // Common name associated with cert
+ issuer[256]; // Issuer associated with cert
+ size_t len; // Length of string
+ time_t expiration; // Expiration date of cert
char expstr[256]; // Expiration date as string */
- int sigalg; /* Signature algorithm */
- unsigned char md5_digest[16]; /* MD5 result */
+ int sigalg; // Signature algorithm
+ unsigned char md5_digest[16]; // MD5 result
len = sizeof(name) - 1;
if (gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, name, &len) >= 0)
@@ -682,223 +1316,48 @@ httpCredentialsString(
}
-/*
- * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
- */
-
-bool // O - `true` on success, `false` on error
-httpLoadCredentials(
- const char *path, /* I - Keychain/PKCS#12 path */
- cups_array_t **credentials, /* IO - Credentials */
- const char *common_name) /* I - Common name for credentials */
-{
- cups_file_t *fp; /* Certificate file */
- char filename[1024], /* filename.crt */
- temp[1024], /* Temporary string */
- line[256]; /* Base64-encoded line */
- unsigned char *data = NULL; /* Buffer for cert data */
- size_t alloc_data = 0, /* Bytes allocated */
- num_data = 0; /* Bytes used */
- size_t decoded; /* Bytes decoded */
- int in_certificate = 0;
- /* In a certificate? */
-
-
- if (credentials)
- *credentials = NULL;
-
- if (!credentials || !common_name)
- return (false);
-
- if (!path)
- path = http_gnutls_default_path(temp, sizeof(temp));
- if (!path)
- return (false);
-
- http_gnutls_make_path(filename, sizeof(filename), path, common_name, "crt");
-
- if ((fp = cupsFileOpen(filename, "r")) == NULL)
- return (false);
-
- while (cupsFileGets(fp, line, sizeof(line)))
- {
- if (!strcmp(line, "-----BEGIN CERTIFICATE-----"))
- {
- if (in_certificate)
- {
- /*
- * Missing END CERTIFICATE...
- */
-
- httpFreeCredentials(*credentials);
- *credentials = NULL;
- break;
- }
-
- in_certificate = 1;
- }
- else if (!strcmp(line, "-----END CERTIFICATE-----"))
- {
- if (!in_certificate || !num_data)
- {
- /*
- * Missing data...
- */
-
- httpFreeCredentials(*credentials);
- *credentials = NULL;
- break;
- }
-
- if (!*credentials)
- *credentials = cupsArrayNew(NULL, NULL, NULL, 0, NULL, NULL);
-
- if (httpAddCredential(*credentials, data, num_data))
- {
- httpFreeCredentials(*credentials);
- *credentials = NULL;
- break;
- }
-
- num_data = 0;
- in_certificate = 0;
- }
- else if (in_certificate)
- {
- if (alloc_data == 0)
- {
- data = malloc(2048);
- alloc_data = 2048;
-
- if (!data)
- break;
- }
- else if ((num_data + strlen(line)) >= alloc_data)
- {
- unsigned char *tdata = realloc(data, alloc_data + 1024);
- /* Expanded buffer */
-
- if (!tdata)
- {
- httpFreeCredentials(*credentials);
- *credentials = NULL;
- break;
- }
-
- data = tdata;
- alloc_data += 1024;
- }
-
- decoded = (size_t)(alloc_data - num_data);
- httpDecode64((char *)data + num_data, &decoded, line, NULL);
- num_data += (size_t)decoded;
- }
- }
-
- cupsFileClose(fp);
-
- if (in_certificate)
- {
- /*
- * Missing END CERTIFICATE...
- */
-
- httpFreeCredentials(*credentials);
- *credentials = NULL;
- }
-
- if (data)
- free(data);
-
- return (*credentials != NULL);
-}
-
-
-/*
- * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
- */
+//
+// '_httpFreeCredentials()' - Free internal credentials.
+//
-bool // O - `true` on success, `false` on error
-httpSaveCredentials(
- const char *path, /* I - Keychain/PKCS#12 path */
- cups_array_t *credentials, /* I - Credentials */
- const char *common_name) /* I - Common name for credentials */
+void
+_httpFreeCredentials(
+ http_tls_credentials_t credentials) // I - Internal credentials
{
- cups_file_t *fp; /* Certificate file */
- char filename[1024], /* filename.crt */
- nfilename[1024],/* filename.crt.N */
- temp[1024], /* Temporary string */
- line[256]; /* Base64-encoded line */
- const unsigned char *ptr; /* Pointer into certificate */
- ssize_t remaining; /* Bytes left */
- http_credential_t *cred; /* Current credential */
-
-
- if (!credentials || !common_name)
- return (false);
-
- if (!path)
- path = http_gnutls_default_path(temp, sizeof(temp));
- if (!path)
- return (false);
-
- http_gnutls_make_path(filename, sizeof(filename), path, common_name, "crt");
- snprintf(nfilename, sizeof(nfilename), "%s.N", filename);
-
- if ((fp = cupsFileOpen(nfilename, "w")) == NULL)
- return (false);
-
- fchmod(cupsFileNumber(fp), 0600);
-
- for (cred = (http_credential_t *)cupsArrayGetFirst(credentials);
- cred;
- cred = (http_credential_t *)cupsArrayGetNext(credentials))
- {
- cupsFilePuts(fp, "-----BEGIN CERTIFICATE-----\n");
- for (ptr = cred->data, remaining = (ssize_t)cred->datalen; remaining > 0; remaining -= 45, ptr += 45)
- {
- httpEncode64(line, sizeof(line), (char *)ptr, remaining > 45 ? 45 : (size_t)remaining, false);
- cupsFilePrintf(fp, "%s\n", line);
- }
- cupsFilePuts(fp, "-----END CERTIFICATE-----\n");
- }
-
- cupsFileClose(fp);
-
- return (!rename(nfilename, filename));
+ (void)credentials;
}
-/*
- * 'http_gnutls_create_credential()' - Create a single credential in the internal format.
- */
+//
+// 'gnutls_create_credential()' - Create a single credential in the internal format.
+//
-static gnutls_x509_crt_t /* O - Certificate */
-http_gnutls_create_credential(
- http_credential_t *credential) /* I - Credential */
+static gnutls_x509_crt_t // O - Certificate
+gnutls_create_credential(
+ http_credential_t *credential) // I - Credential
{
- int result; /* Result from GNU TLS */
- gnutls_x509_crt_t cert; /* Certificate */
- gnutls_datum_t datum; /* Data record */
+ int err; // Result from GNU TLS
+ gnutls_x509_crt_t cert; // Certificate
+ gnutls_datum_t datum; // Data record
- DEBUG_printf(("3http_gnutls_create_credential(credential=%p)", credential));
+ DEBUG_printf(("3gnutls_create_credential(credential=%p)", credential));
if (!credential)
return (NULL);
- if ((result = gnutls_x509_crt_init(&cert)) < 0)
+ if ((err = gnutls_x509_crt_init(&cert)) < 0)
{
- DEBUG_printf(("4http_gnutls_create_credential: init error: %s", gnutls_strerror(result)));
+ DEBUG_printf(("4gnutls_create_credential: init error: %s", gnutls_strerror(err)));
return (NULL);
}
datum.data = credential->data;
datum.size = credential->datalen;
- if ((result = gnutls_x509_crt_import(cert, &datum, GNUTLS_X509_FMT_DER)) < 0)
+ if ((err = gnutls_x509_crt_import(cert, &datum, GNUTLS_X509_FMT_DER)) < 0)
{
- DEBUG_printf(("4http_gnutls_create_credential: import error: %s", gnutls_strerror(result)));
+ DEBUG_printf(("4gnutls_create_credential: import error: %s", gnutls_strerror(err)));
gnutls_x509_crt_deinit(cert);
return (NULL);
@@ -908,79 +1367,72 @@ http_gnutls_create_credential(
}
-/*
- * 'http_gnutls_default_path()' - Get the default credential store path.
- */
+//
+// 'gnutls_create_key()' - Create a private key.
+//
-static const char * /* O - Path or NULL on error */
-http_gnutls_default_path(char *buffer,/* I - Path buffer */
- size_t bufsize)/* I - Size of path buffer */
+static gnutls_x509_privkey_t // O - Private key
+gnutls_create_key(cups_credtype_t type) // I - Type of key
{
- _cups_globals_t *cg = _cupsGlobals();
- /* Pointer to library globals */
-
+ gnutls_x509_privkey_t key; // Private key
- if (cg->userconfig)
- {
- if (mkdir(cg->userconfig, 0755) && errno != EEXIST)
- {
- DEBUG_printf(("1http_gnutls_default_path: Failed to make directory '%s': %s", cg->userconfig, strerror(errno)));
- return (NULL);
- }
- snprintf(buffer, bufsize, "%s/ssl", cg->userconfig);
+ gnutls_x509_privkey_init(&key);
- if (mkdir(buffer, 0700) && errno != EEXIST)
- {
- DEBUG_printf(("1http_gnutls_default_path: Failed to make directory '%s': %s", buffer, strerror(errno)));
- return (NULL);
- }
- }
- else
+ switch (type)
{
- if (mkdir(cg->sysconfig, 0755) && errno != EEXIST)
- {
- DEBUG_printf(("1http_gnutls_default_path: Failed to make directory '%s': %s", cg->sysconfig, strerror(errno)));
- return (NULL);
- }
-
- snprintf(buffer, bufsize, "%s/ssl", cg->sysconfig);
-
- if (mkdir(buffer, 0700) && errno != EEXIST)
- {
- DEBUG_printf(("1http_gnutls_default_path: Failed to make directory '%s': %s", buffer, strerror(errno)));
- return (NULL);
- }
+ case CUPS_CREDTYPE_ECDSA_P256_SHA256 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_SECP256R1), 0);
+ break;
+
+ case CUPS_CREDTYPE_ECDSA_P384_SHA256 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_SECP384R1), 0);
+ break;
+
+ case CUPS_CREDTYPE_ECDSA_P521_SHA256 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_ECDSA, GNUTLS_CURVE_TO_BITS(GNUTLS_ECC_CURVE_SECP521R1), 0);
+ break;
+
+ case CUPS_CREDTYPE_RSA_2048_SHA256 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 2048, 0);
+ break;
+
+ default :
+ case CUPS_CREDTYPE_RSA_3072_SHA256 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 3072, 0);
+ break;
+
+ case CUPS_CREDTYPE_RSA_4096_SHA256 :
+ gnutls_x509_privkey_generate(key, GNUTLS_PK_RSA, 4096, 0);
+ break;
}
- DEBUG_printf(("1http_gnutls_default_path: Using default path \"%s\".", buffer));
-
- return (buffer);
+ return (key);
}
-/*
- * 'http_gnutls_load_crl()' - Load the certificate revocation list, if any.
- */
+//
+// 'gnutls_load_crl()' - Load the certificate revocation list, if any.
+//
static void
-http_gnutls_load_crl(void)
+gnutls_load_crl(void)
{
cupsMutexLock(&tls_mutex);
if (!gnutls_x509_crl_init(&tls_crl))
{
- cups_file_t *fp; /* CRL file */
- char filename[1024], /* site.crl */
- line[256]; /* Base64-encoded line */
- unsigned char *data = NULL; /* Buffer for cert data */
- size_t alloc_data = 0, /* Bytes allocated */
- num_data = 0; /* Bytes used */
- size_t decoded; /* Bytes decoded */
- gnutls_datum_t datum; /* Data record */
+ cups_file_t *fp; // CRL file
+ char filename[1024], // site.crl
+ line[256]; // Base64-encoded line
+ unsigned char *data = NULL; // Buffer for cert data
+ size_t alloc_data = 0, // Bytes allocated
+ num_data = 0; // Bytes used
+ size_t decoded; // Bytes decoded
+ gnutls_datum_t datum; // Data record
- http_gnutls_make_path(filename, sizeof(filename), CUPS_SERVERROOT, "site", "crl");
+ http_make_path(filename, sizeof(filename), CUPS_SERVERROOT, "site", "crl");
if ((fp = cupsFileOpen(filename, "r")) != NULL)
{
@@ -990,10 +1442,7 @@ http_gnutls_load_crl(void)
{
if (num_data)
{
- /*
- * Missing END X509 CRL...
- */
-
+ // Missing END X509 CRL...
break;
}
}
@@ -1001,10 +1450,7 @@ http_gnutls_load_crl(void)
{
if (!num_data)
{
- /*
- * Missing data...
- */
-
+ // Missing data...
break;
}
@@ -1028,7 +1474,7 @@ http_gnutls_load_crl(void)
else if ((num_data + strlen(line)) >= alloc_data)
{
unsigned char *tdata = realloc(data, alloc_data + 1024);
- /* Expanded buffer */
+ // Expanded buffer
if (!tdata)
break;
@@ -1054,68 +1500,27 @@ http_gnutls_load_crl(void)
}
-/*
- * 'http_gnutls_make_path()' - Format a filename for a certificate or key file.
- */
-
-static const char * /* O - Filename */
-http_gnutls_make_path(
- char *buffer, /* I - Filename buffer */
- size_t bufsize, /* I - Size of buffer */
- const char *dirname, /* I - Directory */
- const char *filename, /* I - Filename (usually hostname) */
- const char *ext) /* I - Extension */
-{
- char *bufptr, /* Pointer into buffer */
- *bufend = buffer + bufsize - 1; /* End of buffer */
-
-
- snprintf(buffer, bufsize, "%s/", dirname);
- bufptr = buffer + strlen(buffer);
-
- while (*filename && bufptr < bufend)
- {
- if (_cups_isalnum(*filename) || *filename == '-' || *filename == '.')
- *bufptr++ = *filename;
- else
- *bufptr++ = '_';
-
- filename ++;
- }
-
- if (bufptr < bufend)
- *bufptr++ = '.';
-
- cupsCopyString(bufptr, ext, (size_t)(bufend - bufptr + 1));
-
- return (buffer);
-}
-
-
-/*
- * 'http_gnutls_read()' - Read function for the GNU TLS library.
- */
+//
+// 'gnutls_http_read()' - Read function for the GNU TLS library.
+//
-static ssize_t /* O - Number of bytes read or -1 on error */
-http_gnutls_read(
- gnutls_transport_ptr_t ptr, /* I - Connection to server */
- void *data, /* I - Buffer */
- size_t length) /* I - Number of bytes to read */
+static ssize_t // O - Number of bytes read or -1 on error
+gnutls_http_read(
+ gnutls_transport_ptr_t ptr, // I - Connection to server
+ void *data, // I - Buffer
+ size_t length) // I - Number of bytes to read
{
- http_t *http; /* HTTP connection */
- ssize_t bytes; /* Bytes read */
+ http_t *http; // HTTP connection
+ ssize_t bytes; // Bytes read
- DEBUG_printf(("5http_gnutls_read(ptr=%p, data=%p, length=%d)", ptr, data, (int)length));
+ DEBUG_printf(("5gnutls_http_read(ptr=%p, data=%p, length=%d)", ptr, data, (int)length));
http = (http_t *)ptr;
if (!http->blocking || http->timeout_value > 0.0)
{
- /*
- * Make sure we have data before we read...
- */
-
+ // Make sure we have data before we read...
while (!_httpWait(http, http->wait_value, 0))
{
if (http->timeout_cb && (*http->timeout_cb)(http, http->timeout_data))
@@ -1127,79 +1532,73 @@ http_gnutls_read(
}
bytes = recv(http->fd, data, length, 0);
- DEBUG_printf(("5http_gnutls_read: bytes=%d", (int)bytes));
+ DEBUG_printf(("5gnutls_http_read: bytes=%d", (int)bytes));
return (bytes);
}
-/*
- * 'http_gnutls_write()' - Write function for the GNU TLS library.
- */
+//
+// 'gnutls_http_write()' - Write function for the GNU TLS library.
+//
-static ssize_t /* O - Number of bytes written or -1 on error */
-http_gnutls_write(
- gnutls_transport_ptr_t ptr, /* I - Connection to server */
- const void *data, /* I - Data buffer */
- size_t length) /* I - Number of bytes to write */
+static ssize_t // O - Number of bytes written or -1 on error
+gnutls_http_write(
+ gnutls_transport_ptr_t ptr, // I - Connection to server
+ const void *data, // I - Data buffer
+ size_t length) // I - Number of bytes to write
{
- ssize_t bytes; /* Bytes written */
+ ssize_t bytes; // Bytes written
- DEBUG_printf(("5http_gnutls_write(ptr=%p, data=%p, length=%d)", ptr, data,
+ DEBUG_printf(("5gnutls_http_write(ptr=%p, data=%p, length=%d)", ptr, data,
(int)length));
bytes = send(((http_t *)ptr)->fd, data, length, 0);
- DEBUG_printf(("5http_gnutls_write: bytes=%d", (int)bytes));
+ DEBUG_printf(("5gnutls_http_write: bytes=%d", (int)bytes));
return (bytes);
}
-/*
- * '_httpTLSInitialize()' - Initialize the TLS stack.
- */
+//
+// '_httpTLSInitialize()' - Initialize the TLS stack.
+//
void
_httpTLSInitialize(void)
{
- /*
- * Initialize GNU TLS...
- */
-
+ // Initialize GNU TLS...
gnutls_global_init();
}
-/*
- * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
- */
+//
+// '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
+//
-size_t /* O - Bytes available */
-_httpTLSPending(http_t *http) /* I - HTTP connection */
+size_t // O - Bytes available
+_httpTLSPending(http_t *http) // I - HTTP connection
{
return (gnutls_record_check_pending(http->tls));
}
-/*
- * '_httpTLSRead()' - Read from a SSL/TLS connection.
- */
+//
+// '_httpTLSRead()' - Read from a SSL/TLS connection.
+//
-int /* O - Bytes read */
-_httpTLSRead(http_t *http, /* I - Connection to server */
- char *buf, /* I - Buffer to store data */
- int len) /* I - Length of buffer */
+int // O - Bytes read
+_httpTLSRead(http_t *http, // I - Connection to server
+ char *buf, // I - Buffer to store data
+ int len) // I - Length of buffer
{
- ssize_t result; /* Return value */
+ ssize_t result; // Return value
result = gnutls_record_recv(http->tls, buf, (size_t)len);
if (result < 0 && !errno)
{
- /*
- * Convert GNU TLS error to errno value...
- */
-
+ // Convert GNU TLS error to errno value...
switch (result)
{
case GNUTLS_E_INTERRUPTED :
@@ -1222,43 +1621,25 @@ _httpTLSRead(http_t *http, /* I - Connection to server */
}
-/*
- * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
- */
-
-void
-_httpTLSSetOptions(int options, /* I - Options */
- int min_version, /* I - Minimum TLS version */
- int max_version) /* I - Maximum TLS version */
-{
- if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
- {
- tls_options = options;
- tls_min_version = min_version;
- tls_max_version = max_version;
- }
-}
-
-
-/*
- * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
- */
+//
+// '_httpTLSStart()' - Set up SSL/TLS support on a connection.
+//
-bool /* O - `true` on success, `false` on failure */
-_httpTLSStart(http_t *http) /* I - Connection to server */
+bool // O - `true` on success, `false` on failure
+_httpTLSStart(http_t *http) // I - Connection to server
{
- char hostname[256], /* Hostname */
- *hostptr; /* Pointer into hostname */
- int status; /* Status of handshake */
+ char hostname[256], // Hostname
+ *hostptr; // Pointer into hostname
+ int status; // Status of handshake
gnutls_certificate_credentials_t *credentials;
- /* TLS credentials */
+ // TLS credentials
char priority_string[2048];
- /* Priority string */
- int version; /* Current version */
- double old_timeout; /* Old timeout value */
- http_timeout_cb_t old_cb; /* Old timeout callback */
- void *old_data; /* Old timeout data */
- static const char * const versions[] =/* SSL/TLS versions */
+ // Priority string
+ int version; // Current version
+ double old_timeout; // Old timeout value
+ http_timeout_cb_t old_cb; // Old timeout callback
+ void *old_data; // Old timeout data
+ static const char * const versions[] =// SSL/TLS versions
{
"VERS-SSL3.0",
"VERS-TLS1.0",
@@ -1324,23 +1705,16 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
if (http->mode == _HTTP_MODE_CLIENT)
{
- /*
- * Client: get the hostname to use for TLS...
- */
-
+ // Client: get the hostname to use for TLS...
if (httpAddrIsLocalhost(http->hostaddr))
{
cupsCopyString(hostname, "localhost", sizeof(hostname));
}
else
{
- /*
- * Otherwise make sure the hostname we have does not end in a trailing dot.
- */
-
+ // Otherwise make sure the hostname we have does not end in a trailing dot.
cupsCopyString(hostname, http->hostname, sizeof(hostname));
- if ((hostptr = hostname + strlen(hostname) - 1) >= hostname &&
- *hostptr == '.')
+ if ((hostptr = hostname + strlen(hostname) - 1) >= hostname && *hostptr == '.')
*hostptr = '\0';
}
@@ -1348,10 +1722,7 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
}
else
{
- /*
- * Server: get certificate and private key...
- */
-
+ // Server: get certificate and private key...
char crtfile[1024], // Certificate file
keyfile[1024]; // Private key file
const char *cn, // Common name to lookup
@@ -1360,20 +1731,14 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
if (http->fields[HTTP_FIELD_HOST])
{
- /*
- * Use hostname for TLS upgrade...
- */
-
+ // Use hostname for TLS upgrade...
cupsCopyString(hostname, http->fields[HTTP_FIELD_HOST], sizeof(hostname));
}
else
{
- /*
- * Resolve hostname from connection address...
- */
-
- http_addr_t addr; /* Connection address */
- socklen_t addrlen; /* Length of address */
+ // Resolve hostname from connection address...
+ http_addr_t addr; // Connection address
+ socklen_t addrlen; // Length of address
addrlen = sizeof(addr);
if (getsockname(http->fd, (struct sockaddr *)&addr, &addrlen))
@@ -1382,7 +1747,9 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
hostname[0] = '\0';
}
else if (httpAddrIsLocalhost(&addr))
+ {
hostname[0] = '\0';
+ }
else
{
httpAddrLookup(&addr, hostname, sizeof(hostname));
@@ -1391,7 +1758,7 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
}
if (isdigit(hostname[0] & 255) || hostname[0] == '[')
- hostname[0] = '\0'; /* Don't allow numeric addresses */
+ hostname[0] = '\0'; // Don't allow numeric addresses
cupsMutexLock(&tls_mutex);
@@ -1403,13 +1770,13 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
if (cn)
{
// First look in the CUPS keystore...
- http_gnutls_make_path(crtfile, sizeof(crtfile), tls_keypath, cn, "crt");
- http_gnutls_make_path(keyfile, sizeof(keyfile), tls_keypath, cn, "key");
+ http_make_path(crtfile, sizeof(crtfile), tls_keypath, cn, "crt");
+ http_make_path(keyfile, sizeof(keyfile), tls_keypath, cn, "key");
if (access(crtfile, R_OK) || access(keyfile, R_OK))
{
// No CUPS-managed certs, look for CA certs...
- char cacrtfile[1024], cakeyfile[1024]; /* CA cert files */
+ char cacrtfile[1024], cakeyfile[1024]; // CA cert files
snprintf(cacrtfile, sizeof(cacrtfile), "/etc/letsencrypt/live/%s/fullchain.pem", cn);
snprintf(cakeyfile, sizeof(cakeyfile), "/etc/letsencrypt/live/%s/privkey.pem", cn);
@@ -1440,9 +1807,9 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
{
DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", cn));
- if (!cupsMakeServerCredentials(tls_keypath, cn, 0, NULL, time(NULL) + 3650 * 86400))
+ if (!cupsCreateCredentials(tls_keypath, false, CUPS_CREDPURPOSE_SERVER_AUTH, CUPS_CREDTYPE_DEFAULT, CUPS_CREDUSAGE_DEFAULT_TLS, NULL, NULL, NULL, NULL, NULL, cn, 0, NULL, NULL, time(NULL) + 3650 * 86400))
{
- DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
+ DEBUG_puts("4_httpTLSStart: cupsCreateCredentials failed.");
http->error = errno = EINVAL;
http->status = HTTP_STATUS_ERROR;
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
@@ -1482,10 +1849,7 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
if (tls_max_version < _HTTP_TLS_MAX)
{
- /*
- * Require specific TLS versions...
- */
-
+ // Require specific TLS versions...
cupsConcatString(priority_string, ":-VERS-TLS-ALL", sizeof(priority_string));
for (version = tls_min_version; version <= tls_max_version; version ++)
{
@@ -1495,18 +1859,12 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
}
else if (tls_min_version == _HTTP_TLS_SSL3)
{
- /*
- * Allow all versions of TLS and SSL/3.0...
- */
-
+ // Allow all versions of TLS and SSL/3.0...
cupsConcatString(priority_string, ":+VERS-TLS-ALL:+VERS-SSL3.0", sizeof(priority_string));
}
else
{
- /*
- * Require a minimum version...
- */
-
+ // Require a minimum version...
cupsConcatString(priority_string, ":+VERS-TLS-ALL", sizeof(priority_string));
for (version = 0; version < tls_min_version; version ++)
{
@@ -1529,24 +1887,21 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
gnutls_priority_set_direct(http->tls, priority_string, NULL);
#else
- gnutls_priority_t priority; /* Priority */
+ gnutls_priority_t priority; // Priority
gnutls_priority_init(&priority, priority_string, NULL);
gnutls_priority_set(http->tls, priority);
gnutls_priority_deinit(priority);
-#endif /* HAVE_GNUTLS_PRIORITY_SET_DIRECT */
+#endif // HAVE_GNUTLS_PRIORITY_SET_DIRECT
gnutls_transport_set_ptr(http->tls, (gnutls_transport_ptr_t)http);
- gnutls_transport_set_pull_function(http->tls, http_gnutls_read);
+ gnutls_transport_set_pull_function(http->tls, gnutls_http_read);
#ifdef HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION
gnutls_transport_set_pull_timeout_function(http->tls, (gnutls_pull_timeout_func)httpWait);
-#endif /* HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION */
- gnutls_transport_set_push_function(http->tls, http_gnutls_write);
-
- /*
- * Enforce a minimum timeout of 10 seconds for the TLS handshake...
- */
+#endif // HAVE_GNUTLS_TRANSPORT_SET_PULL_TIMEOUT_FUNCTION
+ gnutls_transport_set_push_function(http->tls, gnutls_http_write);
+ // Enforce a minimum timeout of 10 seconds for the TLS handshake...
old_timeout = http->timeout_value;
old_cb = http->timeout_cb;
old_data = http->timeout_data;
@@ -1557,10 +1912,7 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
httpSetTimeout(http, 10.0, NULL, NULL);
}
- /*
- * Do the TLS handshake...
- */
-
+ // Do the TLS handshake...
while ((status = gnutls_handshake(http->tls)) != GNUTLS_E_SUCCESS)
{
DEBUG_printf(("5_httpStartTLS: gnutls_handshake returned %d (%s)",
@@ -1584,10 +1936,7 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
}
}
- /*
- * Restore the previous timeout settings...
- */
-
+ // Restore the previous timeout settings...
httpSetTimeout(http, old_timeout, old_cb, old_data);
http->tls_credentials = credentials;
@@ -1596,14 +1945,14 @@ _httpTLSStart(http_t *http) /* I - Connection to server */
}
-/*
- * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
- */
+//
+// '_httpTLSStop()' - Shut down SSL/TLS on a connection.
+//
void
-_httpTLSStop(http_t *http) /* I - Connection to server */
+_httpTLSStop(http_t *http) // I - Connection to server
{
- int error; /* Error code */
+ int error; // Error code
error = gnutls_bye(http->tls, http->mode == _HTTP_MODE_CLIENT ? GNUTLS_SHUT_RDWR : GNUTLS_SHUT_WR);
@@ -1622,16 +1971,16 @@ _httpTLSStop(http_t *http) /* I - Connection to server */
}
-/*
- * '_httpTLSWrite()' - Write to a SSL/TLS connection.
- */
+//
+// '_httpTLSWrite()' - Write to a SSL/TLS connection.
+//
-int /* O - Bytes written */
-_httpTLSWrite(http_t *http, /* I - Connection to server */
- const char *buf, /* I - Buffer holding data */
- int len) /* I - Length of buffer */
+int // O - Bytes written
+_httpTLSWrite(http_t *http, // I - Connection to server
+ const char *buf, // I - Buffer holding data
+ int len) // I - Length of buffer
{
- ssize_t result; /* Return value */
+ ssize_t result; // Return value
DEBUG_printf(("5_httpTLSWrite(http=%p, buf=%p, len=%d)", http, buf, len));
@@ -1640,10 +1989,7 @@ _httpTLSWrite(http_t *http, /* I - Connection to server */
if (result < 0 && !errno)
{
- /*
- * Convert GNU TLS error to errno value...
- */
-
+ // Convert GNU TLS error to errno value...
switch (result)
{
case GNUTLS_E_INTERRUPTED :
diff --git a/cups/tls-openssl.c b/cups/tls-openssl.c
index 258b4eee7..b071b33e1 100644
--- a/cups/tls-openssl.c
+++ b/cups/tls-openssl.c
@@ -1,28 +1,26 @@
-/*
- * TLS support code for CUPS using OpenSSL/LibreSSL.
- *
- * Copyright © 2020-2022 by OpenPrinting
- * Copyright © 2007-2019 by Apple Inc.
- * Copyright © 1997-2007 by Easy Software Products, all rights reserved.
- *
- * Licensed under Apache License v2.0. See the file "LICENSE" for more
- * information.
- */
-
-/**** This file is included from tls.c ****/
-
-/*
- * Include necessary headers...
- */
-
-#include
+//
+// TLS support code for CUPS using OpenSSL/LibreSSL.
+//
+// Note: This file is included from tls.c
+//
+// Copyright © 2020-2023 by OpenPrinting
+// Copyright © 2007-2019 by Apple Inc.
+// Copyright © 1997-2007 by Easy Software Products, all rights reserved.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
+
#include
-#define USE_EC 0 // Set to 1 to generate EC certs
+#include
+#include
+#include
-/*
- * Local functions...
- */
+//
+// Local functions...
+//
+
static long http_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2);
static int http_bio_free(BIO *data);
@@ -31,128 +29,178 @@ static int http_bio_puts(BIO *h, const char *str);
static int http_bio_read(BIO *h, char *buf, int size);
static int http_bio_write(BIO *h, const char *buf, int num);
-static X509 *http_create_credential(http_credential_t *credential);
-static const char *http_default_path(char *buffer, size_t bufsize);
-static time_t http_get_date(X509 *cert, int which);
-//static void http_load_crl(void);
-static const char *http_make_path(char *buffer, size_t bufsize, const char *dirname, const char *filename, const char *ext);
-static bool http_x509_add_ext(X509 *cert, int nid, const char *value);
-static void http_x509_add_san(X509 *cert, const char *name);
+static bool openssl_add_ext(STACK_OF(X509_EXTENSION) *exts, int nid, const char *value);
+static X509 *openssl_create_credential(http_credential_t *credential);
+static X509_NAME *openssl_create_name(const char *organization, const char *org_unit, const char *locality, const char *state_province, const char *country, const char *common_name);
+static EVP_PKEY *openssl_create_key(cups_credtype_t type);
+static X509_EXTENSION *openssl_create_san(const char *common_name, size_t num_alt_names, const char * const *alt_names);
+static time_t openssl_get_date(X509 *cert, int which);
+//static void openssl_load_crl(void);
-/*
- * Local globals...
- */
+//
+// Local globals...
+//
-static int tls_auto_create = 0;
- /* Auto-create self-signed certs? */
static BIO_METHOD *tls_bio_method = NULL;
- /* OpenSSL BIO method */
-static char *tls_common_name = NULL;
- /* Default common name */
-//static X509_CRL *tls_crl = NULL;/* Certificate revocation list */
-static char *tls_keypath = NULL;
- /* Server cert keychain path */
-static cups_mutex_t tls_mutex = CUPS_MUTEX_INITIALIZER;
- /* Mutex for keychain/certs */
-static int tls_options = -1,/* Options for TLS connections */
- tls_min_version = _HTTP_TLS_1_2,
- tls_max_version = _HTTP_TLS_MAX;
-
-
-/*
- * 'cupsMakeServerCredentials()' - Make a self-signed certificate and private key pair.
- */
-
-int // O - 1 on success, 0 on failure
-cupsMakeServerCredentials(
- const char *path, // I - Path to keychain/directory
- const char *common_name, // I - Common name
- int num_alt_names, // I - Number of subject alternate names
- const char **alt_names, // I - Subject Alternate Names
- time_t expiration_date) // I - Expiration date
+ // OpenSSL BIO method
+static const char * const tls_purpose_oids[] =
+{ // OIDs for each key purpose value
+ "1.3.6.1.5.5.7.3.1", // serverAuth
+ "1.3.6.1.5.5.7.3.2", // clientAuth
+ "1.3.6.1.5.5.7.3.3", // codeSigning
+ "1.3.6.1.5.5.7.3.4", // emailProtection
+ "1.3.6.1.5.5.7.3.8", // timeStamping
+ "1.3.6.1.5.5.7.3.9" // OCSPSigning
+};
+static const char * const tls_usage_strings[] =
+{ // Strings for each key usage value
+ "digitalSignature",
+ "nonRepudiation",
+ "keyEncipherment",
+ "dataEncipherment",
+ "keyAgreement",
+ "keyCertSign",
+ "cRLSign",
+ "encipherOnly",
+ "decipherOnly"
+};
+
+
+//
+// 'cupsCreateCredentials()' - Make an X.509 certificate and private key pair.
+//
+// This function creates an X.509 certificate and private key pair. The
+// certificate and key are stored in the directory "path" or, if "path" is
+// `NULL`, in a per-user or system-wide (when running as root) certificate/key
+// store. The generated certificate is signed by the named root certificate or,
+// if "root_name" is `NULL`, a site-wide default root certificate. When
+// "root_name" is `NULL` and there is no site-wide default root certificate, a
+// self-signed certificate is generated instead.
+//
+// The "ca_cert" argument specifies whether a CA certificate should be created.
+//
+// The "purpose" argument specifies the purpose(s) used for the credentials as a
+// bitwise OR of the following constants:
+//
+// - `CUPS_CREDPURPOSE_SERVER_AUTH` for validating TLS servers,
+// - `CUPS_CREDPURPOSE_CLIENT_AUTH` for validating TLS clients,
+// - `CUPS_CREDPURPOSE_CODE_SIGNING` for validating compiled code,
+// - `CUPS_CREDPURPOSE_EMAIL_PROTECTION` for validating email messages,
+// - `CUPS_CREDPURPOSE_TIME_STAMPING` for signing timestamps to objects, and/or
+// - `CUPS_CREDPURPOSE_OCSP_SIGNING` for Online Certificate Status Protocol
+// message signing.
+//
+// The "type" argument specifies the type of credentials using one of the
+// following constants:
+//
+// - `CUPS_CREDTYPE_DEFAULT`: default type (RSA-3072 or P-384),
+// - `CUPS_CREDTYPE_RSA_2048_SHA256`: RSA with 2048-bit keys and SHA-256 hash,
+// - `CUPS_CREDTYPE_RSA_3072_SHA256`: RSA with 3072-bit keys and SHA-256 hash,
+// - `CUPS_CREDTYPE_RSA_4096_SHA256`: RSA with 4096-bit keys and SHA-256 hash,
+// - `CUPS_CREDTYPE_ECDSA_P256_SHA256`: ECDSA using the P-256 curve with SHA-256 hash,
+// - `CUPS_CREDTYPE_ECDSA_P384_SHA256`: ECDSA using the P-384 curve with SHA-256 hash, or
+// - `CUPS_CREDTYPE_ECDSA_P521_SHA256`: ECDSA using the P-521 curve with SHA-256 hash.
+//
+// The "usage" argument specifies the usage(s) for the credentials as a bitwise
+// OR of the following constants:
+//
+// - `CUPS_CREDUSAGE_DIGITAL_SIGNATURE`: digital signatures,
+// - `CUPS_CREDUSAGE_NON_REPUDIATION`: non-repudiation/content commitment,
+// - `CUPS_CREDUSAGE_KEY_ENCIPHERMENT`: key encipherment,
+// - `CUPS_CREDUSAGE_DATA_ENCIPHERMENT`: data encipherment,
+// - `CUPS_CREDUSAGE_KEY_AGREEMENT`: key agreement,
+// - `CUPS_CREDUSAGE_KEY_CERT_SIGN`: key certicate signing,
+// - `CUPS_CREDUSAGE_CRL_SIGN`: certificate revocation list signing,
+// - `CUPS_CREDUSAGE_ENCIPHER_ONLY`: encipherment only,
+// - `CUPS_CREDUSAGE_DECIPHER_ONLY`: decipherment only,
+// - `CUPS_CREDUSAGE_DEFAULT_CA`: defaults for CA certificates,
+// - `CUPS_CREDUSAGE_DEFAULT_TLS`: defaults for TLS certificates, and/or
+// - `CUPS_CREDUSAGE_ALL`: all usages.
+//
+// The "organization", "org_unit", "locality", "state_province", and "country"
+// arguments specify information about the identity and geolocation of the
+// issuer.
+//
+// The "common_name" argument specifies the common name and the "num_alt_names"
+// and "alt_names" arguments specify a list of DNS hostnames for the
+// certificate.
+//
+// The "expiration_date" argument specifies the expiration date and time as a
+// Unix `time_t` value in seconds.
+//
+
+bool // O - `true` on success, `false` on failure
+cupsCreateCredentials(
+ const char *path, // I - Directory path for certificate/key store or `NULL` for default
+ bool ca_cert, // I - `true` to create a CA certificate, `false` for a client/server certificate
+ cups_credpurpose_t purpose, // I - Credential purposes
+ cups_credtype_t type, // I - Credential type
+ cups_credusage_t usage, // I - Credential usages
+ const char *organization, // I - Organization or `NULL` to use common name
+ const char *org_unit, // I - Organizational unit or `NULL` for none
+ const char *locality, // I - City/town or `NULL` for "Unknown"
+ const char *state_province, // I - State/province or `NULL` for "Unknown"
+ const char *country, // I - Country or `NULL` for locale-based default
+ const char *common_name, // I - Common name
+ size_t num_alt_names, // I - Number of subject alternate names
+ const char * const *alt_names, // I - Subject Alternate Names
+ const char *root_name, // I - Root certificate/domain name or `NULL` for site/self-signed
+ time_t expiration_date) // I - Expiration date
{
- int result = 0; // Return value
- EVP_PKEY *pkey; // Private key
-#if defined(EVP_PKEY_EC) && USE_EC
- EC_KEY *ec; // EC key
-#else
- RSA *rsa; // RSA key pair
-#endif // EVP_PKEY_EC && USE_EC
+ bool result = false; // Return value
+ EVP_PKEY *pkey; // Key pair
X509 *cert; // Certificate
- cups_lang_t *language; // Default language info
- const char *langname; // Language name
+ X509 *root_cert = NULL; // Root certificate, if any
+ EVP_PKEY *root_key = NULL; // Root private key, if any
+ char defpath[1024], // Default path
+ crtfile[1024], // Certificate filename
+ keyfile[1024], // Private key filename
+ root_crtfile[1024], // Root certificate filename
+ root_keyfile[1024]; // Root private key filename
time_t curtime; // Current time
X509_NAME *name; // Subject/issuer name
ASN1_INTEGER *serial; // Serial number
ASN1_TIME *notBefore, // Initial date
*notAfter; // Expiration date
BIO *bio; // Output file
- char temp[1024], // Temporary directory name
- crtfile[1024], // Certificate filename
- keyfile[1024]; // Private key filename
+ char temp[1024], // Temporary string
+ *tempptr; // Pointer into temporary string
+ STACK_OF(X509_EXTENSION) *exts; // Extensions
+ X509_EXTENSION *ext; // Current extension
+ unsigned i; // Looping var
+ cups_credpurpose_t purpose_bit; // Current purpose
+ cups_credusage_t usage_bit; // Current usage
- DEBUG_printf(("cupsMakeServerCredentials(path=\"%s\", common_name=\"%s\", num_alt_names=%d, alt_names=%p, expiration_date=%d)", path, common_name, num_alt_names, alt_names, (int)expiration_date));
+ DEBUG_printf(("cupsCreateCredentials(path=\"%s\", ca_cert=%s, purpose=0x%x, type=%d, usage=0x%x, organization=\"%s\", org_unit=\"%s\", locality=\"%s\", state_province=\"%s\", country=\"%s\", common_name=\"%s\", num_alt_names=%u, alt_names=%p, root_name=\"%s\", expiration_date=%ld)", path, ca_cert ? "true" : "false", purpose, type, usage, organization, org_unit, locality, state_province, country, common_name, (unsigned)num_alt_names, alt_names, root_name, (long)expiration_date));
// Filenames...
if (!path)
- path = http_default_path(temp, sizeof(temp));
+ path = http_default_path(defpath, sizeof(defpath));
if (!path || !common_name)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
- return (0);
+ return (false);
}
- http_make_path(crtfile, sizeof(crtfile), path, common_name, "crt");
- http_make_path(keyfile, sizeof(keyfile), path, common_name, "key");
-
// Create the encryption key...
- DEBUG_puts("1cupsMakeServerCredentials: Creating key pair.");
-
-#if defined(EVP_PKEY_EC) && USE_EC
- if ((ec = EC_KEY_new_by_curve_name(NID_secp384r1)) == NULL)
- {
- _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create key pair."), 1);
- return (0);
- }
-#else
- if ((rsa = RSA_generate_key(3072, RSA_F4, NULL, NULL)) == NULL)
- {
- _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create key pair."), 1);
- return (0);
- }
-#endif // EVP_PKEY_EC && USE_EC
+ DEBUG_puts("1cupsCreateCredentials: Creating key pair.");
- if ((pkey = EVP_PKEY_new()) == NULL)
- {
-#if defined(EVP_PKEY_EC) && USE_EC
- EC_KEY_free(ec);
-#else
- RSA_free(rsa);
-#endif // EVP_PKEY_EC && USE_EC
-
- _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create private key."), 1);
- return (0);
- }
-
-#if defined(EVP_PKEY_EC) && USE_EC
- EVP_PKEY_assign_EC_KEY(pkey, ec);
-#else
- EVP_PKEY_assign_RSA(pkey, rsa);
-#endif // EVP_PKEY_EC && USE_EC
+ if ((pkey = openssl_create_key(type)) == NULL)
+ return (false);
- DEBUG_puts("1cupsMakeServerCredentials: Key pair created.");
+ DEBUG_puts("1cupsCreateCredentials: Key pair created.");
// Create the X.509 certificate...
- DEBUG_puts("1cupsMakeServerCredentials: Generating self-signed X.509 certificate.");
+ DEBUG_puts("1cupsCreateCredentials: Generating X.509 certificate.");
if ((cert = X509_new()) == NULL)
{
EVP_PKEY_free(pkey);
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create X.509 certificate."), 1);
- return (0);
+ return (false);
}
curtime = time(NULL);
@@ -174,60 +222,112 @@ cupsMakeServerCredentials(
X509_set_pubkey(cert, pkey);
- language = cupsLangDefault();
- langname = cupsLangGetName(language);
- name = X509_NAME_new();
- if (strlen(langname) == 5)
- X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (unsigned char *)langname + 3, -1, -1, 0);
- else
- X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (unsigned char *)"US", -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, SN_commonName, MBSTRING_ASC, (unsigned char *)common_name, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, SN_organizationName, MBSTRING_ASC, (unsigned char *)common_name, -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, SN_organizationalUnitName, MBSTRING_ASC, (unsigned char *)"Unknown", -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, SN_stateOrProvinceName, MBSTRING_ASC, (unsigned char *)"Unknown", -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, SN_localityName, MBSTRING_ASC, (unsigned char *)"Unknown", -1, -1, 0);
+ name = openssl_create_name(organization, org_unit, locality, state_province, country, common_name);
- X509_set_issuer_name(cert, name);
X509_set_subject_name(cert, name);
+
+ // Try loading a root certificate...
+ http_make_path(root_crtfile, sizeof(root_crtfile), path, root_name ? root_name : "_site_", "crt");
+ http_make_path(root_keyfile, sizeof(root_keyfile), path, root_name ? root_name : "_site_", "key");
+
+ if (!ca_cert && !access(root_crtfile, 0) && !access(root_keyfile, 0))
+ {
+ if ((bio = BIO_new_file(root_crtfile, "rb")) != NULL)
+ {
+ PEM_read_bio_X509(bio, &root_cert, /*cb*/NULL, /*u*/NULL);
+ BIO_free(bio);
+
+ if ((bio = BIO_new_file(root_keyfile, "rb")) != NULL)
+ {
+ PEM_read_bio_PrivateKey(bio, &root_key, /*cb*/NULL, /*u*/NULL);
+ BIO_free(bio);
+ }
+
+ if (!root_key)
+ {
+ // Only use root certificate if we have the key...
+ X509_free(root_cert);
+ root_cert = NULL;
+ }
+ }
+
+ if (!root_cert || !root_key)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to load X.509 CA certificate and private key."), 1);
+ goto done;
+ }
+ }
+
+ if (root_cert)
+ X509_set_issuer_name(cert, X509_get_subject_name(root_cert));
+ else
+ X509_set_issuer_name(cert, name);
+
X509_NAME_free(name);
- http_x509_add_san(cert, common_name);
- if (!strstr(common_name, ".local"))
+ exts = sk_X509_EXTENSION_new_null();
+
+ if (ca_cert)
{
- // Add common_name.local to the list, too...
- char localname[256], // hostname.local
- *localptr; // Pointer into localname
+ // Add extensions that are required to make Chrome happy...
+ openssl_add_ext(exts, NID_basic_constraints, "critical,CA:TRUE,pathlen:0");
+ }
+ else
+ {
+ // Add extension with DNS names and free buffer for GENERAL_NAME
+ if ((ext = openssl_create_san(common_name, num_alt_names, alt_names)) == NULL)
+ goto done;
- cupsCopyString(localname, common_name, sizeof(localname));
- if ((localptr = strchr(localname, '.')) != NULL)
- *localptr = '\0';
- cupsConcatString(localname, ".local", sizeof(localname));
+ sk_X509_EXTENSION_push(exts, ext);
- http_x509_add_san(cert, localname);
+ // Add extensions that are required to make Chrome happy...
+ openssl_add_ext(exts, NID_basic_constraints, "critical,CA:FALSE,pathlen:0");
}
- if (num_alt_names > 0)
+ cupsCopyString(temp, "critical", sizeof(temp));
+ for (tempptr = temp + strlen(temp), i = 0, usage_bit = CUPS_CREDUSAGE_DIGITAL_SIGNATURE; i < (sizeof(tls_usage_strings) / sizeof(tls_usage_strings[0])); i ++, usage_bit *= 2)
{
- int i; // Looping var...
+ if (!(usage & usage_bit))
+ continue;
- for (i = 0; i < num_alt_names; i ++)
- {
- if (strcmp(alt_names[i], "localhost"))
- http_x509_add_san(cert, alt_names[i]);
- }
+ snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), ",%s", tls_usage_strings[i]);
+
+ tempptr += strlen(tempptr);
+ }
+ openssl_add_ext(exts, NID_key_usage, temp);
+
+ temp[0] = '\0';
+ for (tempptr = temp, i = 0, purpose_bit = CUPS_CREDPURPOSE_SERVER_AUTH; i < (sizeof(tls_purpose_oids) / sizeof(tls_purpose_oids[0])); i ++, purpose_bit *= 2)
+ {
+ if (!(purpose & purpose_bit))
+ continue;
+
+ if (tempptr == temp)
+ cupsCopyString(temp, tls_purpose_oids[i], sizeof(temp));
+ else
+ snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), ",%s", tls_purpose_oids[i]);
+
+ tempptr += strlen(tempptr);
}
+ openssl_add_ext(exts, NID_ext_key_usage, temp);
+
+ openssl_add_ext(exts, NID_subject_key_identifier, "hash");
+ openssl_add_ext(exts, NID_authority_key_identifier, "keyid,issuer");
+
+ while ((ext = sk_X509_EXTENSION_pop(exts)) != NULL)
+ X509_add_ext(cert, ext, -1);
- // Add extensions that are required to make Chrome happy...
- http_x509_add_ext(cert, NID_basic_constraints, "critical,CA:FALSE,pathlen:0");
- http_x509_add_ext(cert, NID_key_usage, "critical,digitalSignature,keyEncipherment");
- http_x509_add_ext(cert, NID_ext_key_usage, "1.3.6.1.5.5.7.3.1");
- http_x509_add_ext(cert, NID_subject_key_identifier, "hash");
- http_x509_add_ext(cert, NID_authority_key_identifier, "keyid,issuer");
X509_set_version(cert, 2); // v3
- X509_sign(cert, pkey, EVP_sha256());
+ if (root_key)
+ X509_sign(cert, root_key, EVP_sha256());
+ else
+ X509_sign(cert, pkey, EVP_sha256());
// Save them...
+ http_make_path(crtfile, sizeof(crtfile), path, common_name, "crt");
+ http_make_path(keyfile, sizeof(keyfile), path, common_name, "key");
+
if ((bio = BIO_new_file(keyfile, "wb")) == NULL)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
@@ -256,10 +356,13 @@ cupsMakeServerCredentials(
goto done;
}
+ if (root_cert)
+ PEM_write_bio_X509(bio, root_cert);
+
BIO_free(bio);
- result = 1;
- DEBUG_puts("1cupsMakeServerCredentials: Successfully created credentials.");
+ result = true;
+ DEBUG_puts("1cupsCreateCredentials: Successfully created credentials.");
// Cleanup...
done:
@@ -267,77 +370,660 @@ cupsMakeServerCredentials(
X509_free(cert);
EVP_PKEY_free(pkey);
+ if (root_cert)
+ X509_free(root_cert);
+ if (root_key)
+ EVP_PKEY_free(root_key);
+
return (result);
}
-/*
- * 'cupsSetServerCredentials()' - Set the default server credentials.
- *
- * Note: The server credentials are used by all threads in the running process.
- * This function is threadsafe.
- */
+//
+// 'cupsCreateCredentialsRequest()' - Make an X.509 Certificate Signing Request.
+//
+// This function creates an X.509 certificate signing request (CSR) and
+// associated private key. The CSR and key are stored in the directory "path"
+// or, if "path" is `NULL`, in a per-user or system-wide (when running as root)
+// certificate/key store.
+//
+// The "purpose" argument specifies the purpose(s) used for the credentials as a
+// bitwise OR of the following constants:
+//
+// - `CUPS_CREDPURPOSE_SERVER_AUTH` for validating TLS servers,
+// - `CUPS_CREDPURPOSE_CLIENT_AUTH` for validating TLS clients,
+// - `CUPS_CREDPURPOSE_CODE_SIGNING` for validating compiled code,
+// - `CUPS_CREDPURPOSE_EMAIL_PROTECTION` for validating email messages,
+// - `CUPS_CREDPURPOSE_TIME_STAMPING` for signing timestamps to objects, and/or
+// - `CUPS_CREDPURPOSE_OCSP_SIGNING` for Online Certificate Status Protocol
+// message signing.
+//
+// The "type" argument specifies the type of credentials using one of the
+// following constants:
+//
+// - `CUPS_CREDTYPE_DEFAULT`: default type (RSA-3072 or P-384),
+// - `CUPS_CREDTYPE_RSA_2048_SHA256`: RSA with 2048-bit keys and SHA-256 hash,
+// - `CUPS_CREDTYPE_RSA_3072_SHA256`: RSA with 3072-bit keys and SHA-256 hash,
+// - `CUPS_CREDTYPE_RSA_4096_SHA256`: RSA with 4096-bit keys and SHA-256 hash,
+// - `CUPS_CREDTYPE_ECDSA_P256_SHA256`: ECDSA using the P-256 curve with SHA-256 hash,
+// - `CUPS_CREDTYPE_ECDSA_P384_SHA256`: ECDSA using the P-384 curve with SHA-256 hash, or
+// - `CUPS_CREDTYPE_ECDSA_P521_SHA256`: ECDSA using the P-521 curve with SHA-256 hash.
+//
+// The "usage" argument specifies the usage(s) for the credentials as a bitwise
+// OR of the following constants:
+//
+// - `CUPS_CREDUSAGE_DIGITAL_SIGNATURE`: digital signatures,
+// - `CUPS_CREDUSAGE_NON_REPUDIATION`: non-repudiation/content commitment,
+// - `CUPS_CREDUSAGE_KEY_ENCIPHERMENT`: key encipherment,
+// - `CUPS_CREDUSAGE_DATA_ENCIPHERMENT`: data encipherment,
+// - `CUPS_CREDUSAGE_KEY_AGREEMENT`: key agreement,
+// - `CUPS_CREDUSAGE_KEY_CERT_SIGN`: key certicate signing,
+// - `CUPS_CREDUSAGE_CRL_SIGN`: certificate revocation list signing,
+// - `CUPS_CREDUSAGE_ENCIPHER_ONLY`: encipherment only,
+// - `CUPS_CREDUSAGE_DECIPHER_ONLY`: decipherment only,
+// - `CUPS_CREDUSAGE_DEFAULT_CA`: defaults for CA certificates,
+// - `CUPS_CREDUSAGE_DEFAULT_TLS`: defaults for TLS certificates, and/or
+// - `CUPS_CREDUSAGE_ALL`: all usages.
+//
+// The "organization", "org_unit", "locality", "state_province", and "country"
+// arguments specify information about the identity and geolocation of the
+// issuer.
+//
+// The "common_name" argument specifies the common name and the "num_alt_names"
+// and "alt_names" arguments specify a list of DNS hostnames for the
+// certificate.
+//
-int // O - 1 on success, 0 on failure
-cupsSetServerCredentials(
- const char *path, // I - Path to keychain/directory
- const char *common_name, // I - Default common name for server
- int auto_create) // I - 1 = automatically create self-signed certificates
+bool // O - `true` on success, `false` on error
+cupsCreateCredentialsRequest(
+ const char *path, // I - Directory path for certificate/key store or `NULL` for default
+ cups_credpurpose_t purpose, // I - Credential purposes
+ cups_credtype_t type, // I - Credential type
+ cups_credusage_t usage, // I - Credential usages
+ const char *organization, // I - Organization or `NULL` to use common name
+ const char *org_unit, // I - Organizational unit or `NULL` for none
+ const char *locality, // I - City/town or `NULL` for "Unknown"
+ const char *state_province, // I - State/province or `NULL` for "Unknown"
+ const char *country, // I - Country or `NULL` for locale-based default
+ const char *common_name, // I - Common name
+ size_t num_alt_names, // I - Number of subject alternate names
+ const char * const *alt_names) // I - Subject Alternate Names
{
- char temp[1024]; // Default path buffer
-
+ char *result = NULL; // Return value
+ EVP_PKEY *pkey; // Key pair
+ X509_REQ *csr; // Certificate signing request
+ X509_NAME *name; // Subject/issuer name
+ X509_EXTENSION *ext; // X509 extension
+ BIO *bio; // Output file
+ char temp[1024], // Temporary directory name
+ *tempptr, // Pointer into temporary string
+ csrfile[1024], // Certificate signing request filename
+ keyfile[1024]; // Private key filename
+ STACK_OF(X509_EXTENSION) *exts; // Extensions
+ unsigned i; // Looping var
+ cups_credpurpose_t purpose_bit; // Current purpose
+ cups_credusage_t usage_bit; // Current usage
- DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
- /*
- * Use defaults as needed...
- */
+ DEBUG_printf(("cupsCreateCredentialsRequest(path=\"%s\", purpose=0x%x, type=%d, usage=0x%x, organization=\"%s\", org_unit=\"%s\", locality=\"%s\", state_province=\"%s\", country=\"%s\", common_name=\"%s\", num_alt_names=%u, alt_names=%p)", path, purpose, type, usage, organization, org_unit, locality, state_province, country, common_name, (unsigned)num_alt_names, alt_names));
+ // Filenames...
if (!path)
path = http_default_path(temp, sizeof(temp));
- /*
- * Range check input...
- */
-
if (!path || !common_name)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
- return (0);
+ return (false);
}
- cupsMutexLock(&tls_mutex);
+ http_make_path(csrfile, sizeof(csrfile), path, common_name, "csr");
+ http_make_path(keyfile, sizeof(keyfile), path, common_name, "key");
- /*
- * Free old values...
- */
+ // Create the encryption key...
+ DEBUG_puts("1cupsCreateCredentialsRequest: Creating key pair.");
- if (tls_keypath)
- _cupsStrFree(tls_keypath);
+ if ((pkey = openssl_create_key(type)) == NULL)
+ return (false);
- if (tls_common_name)
- _cupsStrFree(tls_common_name);
+ DEBUG_puts("1cupsCreateCredentialsRequest: Key pair created.");
- /*
- * Save the new values...
- */
+ // Create the X.509 certificate...
+ DEBUG_puts("1cupsCreateCredentialsRequest: Generating self-signed X.509 certificate.");
- tls_keypath = _cupsStrAlloc(path);
- tls_auto_create = auto_create;
- tls_common_name = _cupsStrAlloc(common_name);
+ if ((csr = X509_REQ_new()) == NULL)
+ {
+ EVP_PKEY_free(pkey);
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create X.509 certificate signing request."), 1);
+ return (NULL);
+ }
- cupsMutexUnlock(&tls_mutex);
+ X509_REQ_set_pubkey(csr, pkey);
- return (1);
+ if ((name = openssl_create_name(organization, org_unit, locality, state_province, country, common_name)) == NULL)
+ goto done;
+
+ X509_REQ_set_subject_name(csr, name);
+ X509_NAME_free(name);
+
+ // Add extension with DNS names and free buffer for GENERAL_NAME
+ exts = sk_X509_EXTENSION_new_null();
+
+ if ((ext = openssl_create_san(common_name, num_alt_names, alt_names)) == NULL)
+ goto done;
+
+ sk_X509_EXTENSION_push(exts, ext);
+
+ cupsCopyString(temp, "critical", sizeof(temp));
+ for (tempptr = temp + strlen(temp), i = 0, usage_bit = CUPS_CREDUSAGE_DIGITAL_SIGNATURE; i < (sizeof(tls_usage_strings) / sizeof(tls_usage_strings[0])); i ++, usage_bit *= 2)
+ {
+ if (!(usage & usage_bit))
+ continue;
+
+ snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), ",%s", tls_usage_strings[i]);
+
+ tempptr += strlen(tempptr);
+ }
+ openssl_add_ext(exts, NID_key_usage, temp);
+
+ temp[0] = '\0';
+ for (tempptr = temp, i = 0, purpose_bit = CUPS_CREDPURPOSE_SERVER_AUTH; i < (sizeof(tls_purpose_oids) / sizeof(tls_purpose_oids[0])); i ++, purpose_bit *= 2)
+ {
+ if (!(purpose & purpose_bit))
+ continue;
+
+ if (tempptr == temp)
+ cupsCopyString(temp, tls_purpose_oids[i], sizeof(temp));
+ else
+ snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), ",%s", tls_purpose_oids[i]);
+
+ tempptr += strlen(tempptr);
+ }
+ openssl_add_ext(exts, NID_ext_key_usage, temp);
+
+ X509_REQ_add_extensions(csr, exts);
+ X509_REQ_sign(csr, pkey, EVP_sha256());
+
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+
+ // Save them...
+ if ((bio = BIO_new_file(keyfile, "wb")) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ goto done;
+ }
+
+ if (!PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write private key."), 1);
+ BIO_free(bio);
+ goto done;
+ }
+
+ BIO_free(bio);
+
+ if ((bio = BIO_new_file(csrfile, "wb")) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ goto done;
+ }
+
+ if (!PEM_write_bio_X509_REQ(bio, csr))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write X.509 certificate signing request."), 1);
+ BIO_free(bio);
+ goto done;
+ }
+
+ BIO_free(bio);
+
+ // TODO: Copy CSR to returned string
+ result = strdup("");
+ DEBUG_puts("1cupsCreateCredentialsRequest: Successfully created signing request.");
+
+ // Cleanup...
+ done:
+
+ X509_REQ_free(csr);
+ EVP_PKEY_free(pkey);
+
+ return (result);
+}
+
+
+//
+// 'cupsSignCredentialsRequest()' - Sign an X.509 certificate signing request to produce an X.509 certificate chain.
+//
+// This function creates an X.509 certificate from a signing request. The
+// certificate is stored in the directory "path" or, if "path" is `NULL`, in a
+// per-user or system-wide (when running as root) certificate/key store. The
+// generated certificate is signed by the named root certificate or, if
+// "root_name" is `NULL`, a site-wide default root certificate. When
+// "root_name" is `NULL` and there is no site-wide default root certificate, a
+// self-signed certificate is generated instead.
+//
+// The "allowed_purpose" argument specifies the allowed purpose(s) used for the
+// credentials as a bitwise OR of the following constants:
+//
+// - `CUPS_CREDPURPOSE_SERVER_AUTH` for validating TLS servers,
+// - `CUPS_CREDPURPOSE_CLIENT_AUTH` for validating TLS clients,
+// - `CUPS_CREDPURPOSE_CODE_SIGNING` for validating compiled code,
+// - `CUPS_CREDPURPOSE_EMAIL_PROTECTION` for validating email messages,
+// - `CUPS_CREDPURPOSE_TIME_STAMPING` for signing timestamps to objects, and/or
+// - `CUPS_CREDPURPOSE_OCSP_SIGNING` for Online Certificate Status Protocol
+// message signing.
+//
+// The "allowed_usage" argument specifies the allowed usage(s) for the
+// credentials as a bitwise OR of the following constants:
+//
+// - `CUPS_CREDUSAGE_DIGITAL_SIGNATURE`: digital signatures,
+// - `CUPS_CREDUSAGE_NON_REPUDIATION`: non-repudiation/content commitment,
+// - `CUPS_CREDUSAGE_KEY_ENCIPHERMENT`: key encipherment,
+// - `CUPS_CREDUSAGE_DATA_ENCIPHERMENT`: data encipherment,
+// - `CUPS_CREDUSAGE_KEY_AGREEMENT`: key agreement,
+// - `CUPS_CREDUSAGE_KEY_CERT_SIGN`: key certicate signing,
+// - `CUPS_CREDUSAGE_CRL_SIGN`: certificate revocation list signing,
+// - `CUPS_CREDUSAGE_ENCIPHER_ONLY`: encipherment only,
+// - `CUPS_CREDUSAGE_DECIPHER_ONLY`: decipherment only,
+// - `CUPS_CREDUSAGE_DEFAULT_CA`: defaults for CA certificates,
+// - `CUPS_CREDUSAGE_DEFAULT_TLS`: defaults for TLS certificates, and/or
+// - `CUPS_CREDUSAGE_ALL`: all usages.
+//
+// The "cb" and "cb_data" arguments specify a function and its data that are
+// used to validate any subjectAltName values in the signing request:
+//
+// ```
+// bool san_cb(const char *common_name, const char *alt_name, void *cb_data) {
+// ... return true if OK and false if not ...
+// }
+// ```
+//
+// If `NULL`, a default validation function is used that allows "localhost" and
+// variations of the common name.
+//
+// The "expiration_date" argument specifies the expiration date and time as a
+// Unix `time_t` value in seconds.
+//
+
+bool // O - `true` on success, `false` on failure
+cupsSignCredentialsRequest(
+ const char *path, // I - Directory path for certificate/key store or `NULL` for default
+ const char *common_name, // I - Common name to use
+ const char *request, // I - PEM-encoded CSR
+ const char *root_name, // I - Root certificate
+ cups_credpurpose_t allowed_purpose, // I - Allowed credential purpose(s)
+ cups_credusage_t allowed_usage, // I - Allowed credential usage(s)
+ cups_cert_san_cb_t cb, // I - subjectAltName callback or `NULL` to allow just .local
+ void *cb_data, // I - Callback data
+ time_t expiration_date) // I - Certificate expiration date
+{
+ bool result = false; // Return value
+ X509 *cert = NULL; // Certificate
+ X509_REQ *crq = NULL; // Certificate request
+ X509 *root_cert = NULL; // Root certificate, if any
+ EVP_PKEY *root_key = NULL; // Root private key, if any
+ char defpath[1024], // Default path
+ crtfile[1024], // Certificate filename
+ root_crtfile[1024], // Root certificate filename
+ root_keyfile[1024]; // Root private key filename
+ time_t curtime; // Current time
+ ASN1_INTEGER *serial; // Serial number
+ ASN1_TIME *notBefore, // Initial date
+ *notAfter; // Expiration date
+ BIO *bio; // Output file
+ char temp[1024]; // Temporary string
+ int i, j, // Looping vars
+ num_exts; // Number of extensions
+ STACK_OF(X509_EXTENSION) *exts = NULL;// Extensions
+ X509_EXTENSION *ext; // Current extension
+ cups_credpurpose_t purpose; // Current purpose
+ cups_credusage_t usage; // Current usage
+ bool saw_usage = false, // Saw NID_key_usage?
+ saw_ext_usage = false, // Saw NID_ext_key_usage?
+ saw_san = false; // Saw NID_subject_alt_name?
+
+
+ DEBUG_printf(("cupsSignCredentialsRequest(path=\"%s\", common_name=\"%s\", request=\"%s\", root_name=\"%s\", allowed_purpose=0x%x, allowed_usage=0x%x, cb=%p, cb_data=%p, expiration_date=%ld)", path, common_name, request, root_name, allowed_purpose, allowed_usage, cb, cb_data, (long)expiration_date));
+
+ // Filenames...
+ if (!path)
+ path = http_default_path(defpath, sizeof(defpath));
+
+ if (!path || !common_name || !request)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (false);
+ }
+
+ if (!cb)
+ cb = http_default_san_cb;
+
+ // Import the X.509 certificate request...
+ DEBUG_puts("1cupsCreateCredentials: Importing X.509 certificate request.");
+ if ((bio = BIO_new_mem_buf(request, (int)strlen(request))) != NULL)
+ {
+ PEM_read_bio_X509_REQ(bio, &crq, /*cb*/NULL, /*u*/NULL);
+ BIO_free(bio);
+ }
+
+ if (!crq)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to import X.509 certificate request."), 1);
+ return (false);
+ }
+
+ if (X509_REQ_verify(crq, X509_REQ_get_pubkey(crq)) < 0)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to verify X.509 certificate request."), 1);
+ goto done;
+ }
+
+ // Create the X.509 certificate...
+ DEBUG_puts("1cupsSignCredentialsRequest: Generating X.509 certificate.");
+
+ if ((cert = X509_new()) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create X.509 certificate."), 1);
+ goto done;
+ }
+
+ curtime = time(NULL);
+
+ notBefore = ASN1_TIME_new();
+ ASN1_TIME_set(notBefore, curtime);
+ X509_set_notBefore(cert, notBefore);
+ ASN1_TIME_free(notBefore);
+
+ notAfter = ASN1_TIME_new();
+ ASN1_TIME_set(notAfter, expiration_date);
+ X509_set_notAfter(cert, notAfter);
+ ASN1_TIME_free(notAfter);
+
+ serial = ASN1_INTEGER_new();
+ ASN1_INTEGER_set(serial, (int)curtime);
+ X509_set_serialNumber(cert, serial);
+ ASN1_INTEGER_free(serial);
+
+ X509_set_pubkey(cert, X509_REQ_get_pubkey(crq));
+
+ X509_set_subject_name(cert, X509_REQ_get_subject_name(crq));
+ X509_set_version(cert, 2); // v3
+
+ // Copy/verify extensions...
+ exts = X509_REQ_get_extensions(crq);
+ num_exts = sk_X509_EXTENSION_num(exts);
+
+ for (i = 0; i < num_exts; i ++)
+ {
+ // Get the extension object...
+ bool add_ext = false; // Add this extension?
+ ASN1_OBJECT *obj; // Extension object
+ ASN1_OCTET_STRING *extdata; // Extension data string
+ unsigned char *data = NULL; // Extension data bytes
+ int datalen; // Length of extension data
+
+ ext = sk_X509_EXTENSION_value(exts, i);
+ obj = X509_EXTENSION_get_object(ext);
+ extdata = X509_EXTENSION_get_data(ext);
+ datalen = i2d_ASN1_OCTET_STRING(extdata, &data);
+
+#ifdef DEBUG
+ char *tempptr; // Pointer into string
+
+ for (j = 0, tempptr = temp; j < datalen; j ++, tempptr += 2)
+ snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), "%02X", data[j]);
+
+ DEBUG_printf(("1cupsSignCredentialsRequest: EXT%d=%s", OBJ_obj2nid(obj), temp));
+#endif // DEBUG
+
+ switch (OBJ_obj2nid(obj))
+ {
+ case NID_ext_key_usage :
+ add_ext = true;
+ saw_ext_usage = true;
+
+ if (datalen < 12 || data[2] != 0x30 || data[3] != (datalen - 4))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad keyUsage extension in X.509 certificate request."), 1);
+ goto done;
+ }
+
+ for (purpose = 0, j = 4; j < datalen; j += data[j + 1] + 2)
+ {
+ if (data[j] != 0x06 || data[j + 1] != 8 || memcmp(data + j + 2, "+\006\001\005\005\007\003", 7))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad keyUsage extension in X.509 certificate request."), 1);
+ goto done;
+ }
+
+ switch (data[j + 9])
+ {
+ case 1 :
+ purpose |= CUPS_CREDPURPOSE_SERVER_AUTH;
+ break;
+ case 2 :
+ purpose |= CUPS_CREDPURPOSE_CLIENT_AUTH;
+ break;
+ case 3 :
+ purpose |= CUPS_CREDPURPOSE_CODE_SIGNING;
+ break;
+ case 4 :
+ purpose |= CUPS_CREDPURPOSE_EMAIL_PROTECTION;
+ break;
+ case 8 :
+ purpose |= CUPS_CREDPURPOSE_TIME_STAMPING;
+ break;
+ case 9 :
+ purpose |= CUPS_CREDPURPOSE_OCSP_SIGNING;
+ break;
+ default :
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad keyUsage extension in X.509 certificate request."), 1);
+ goto done;
+ }
+ }
+
+ DEBUG_printf(("1cupsSignCredentialsRequest: purpose=0x%04x", purpose));
+
+ if (purpose & ~allowed_purpose)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad keyUsage extension in X.509 certificate request."), 1);
+ goto done;
+ }
+ break;
+
+ case NID_key_usage :
+ add_ext = true;
+ saw_usage = true;
+
+ if (datalen < 6 || datalen > 7 || data[2] != 0x03 || data[3] != (datalen - 4))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad extKeyUsage extension in X.509 certificate request."), 1);
+ goto done;
+ }
+
+ usage = 0;
+ if (data[5] & 0x80)
+ usage |= CUPS_CREDUSAGE_DIGITAL_SIGNATURE;
+ if (data[5] & 0x40)
+ usage |= CUPS_CREDUSAGE_NON_REPUDIATION;
+ if (data[5] & 0x20)
+ usage |= CUPS_CREDUSAGE_KEY_ENCIPHERMENT;
+ if (data[5] & 0x10)
+ usage |= CUPS_CREDUSAGE_DATA_ENCIPHERMENT;
+ if (data[5] & 0x08)
+ usage |= CUPS_CREDUSAGE_KEY_AGREEMENT;
+ if (data[5] & 0x04)
+ usage |= CUPS_CREDUSAGE_KEY_CERT_SIGN;
+ if (data[5] & 0x02)
+ usage |= CUPS_CREDUSAGE_CRL_SIGN;
+ if (data[5] & 0x01)
+ usage |= CUPS_CREDUSAGE_ENCIPHER_ONLY;
+ if (datalen == 7 && (data[6] & 0x80))
+ usage |= CUPS_CREDUSAGE_DECIPHER_ONLY;
+
+ DEBUG_printf(("1cupsSignCredentialsRequest: usage=0x%04x", usage));
+
+ if (usage & ~allowed_usage)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad extKeyUsage extension in X.509 certificate request."), 1);
+ goto done;
+ }
+ break;
+
+ case NID_subject_alt_name :
+ add_ext = true;
+ saw_san = true;
+
+ if (datalen < 4 || data[2] != 0x30 || data[3] != (datalen - 4))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Bad subjectAltName extension in X.509 certificate request."), 1);
+ goto done;
+ }
+
+ // Parse the SAN values (there should be an easier/standard OpenSSL API to do this!)
+ for (j = 4, datalen -= 2; j < datalen; j += data[j + 1] + 2)
+ {
+ if (data[j] == 0x82 && data[j + 1])
+ {
+ // GENERAL_STRING for DNS
+ memcpy(temp, data + j + 2, data[j + 1]);
+ temp[data[j + 1]] = '\0';
+
+ DEBUG_printf(("1cupsSignCredentialsRequest: SAN %s", temp));
+
+ if (!(cb)(common_name, temp, cb_data))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Validation of subjectAltName in X.509 certificate request failed."), 1);
+ goto done;
+ }
+ }
+ }
+ break;
+ }
+
+ OPENSSL_free(data);
+
+ // If we get this far, the object is OK and we can add it...
+ if (add_ext && !X509_add_ext(cert, ext, -1))
+ goto done;
+ }
+
+ // Add basic constraints for an "edge" certificate...
+ if ((ext = X509V3_EXT_conf_nid(/*conf*/NULL, /*ctx*/NULL, NID_basic_constraints, "critical,CA:FALSE,pathlen:0")) == NULL || !X509_add_ext(cert, ext, -1))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to add extension to X.509 certificate."), 1);
+ goto done;
+ }
+
+ // Add key usage extensions as needed...
+ if (!saw_usage)
+ {
+ if ((ext = X509V3_EXT_conf_nid(/*conf*/NULL, /*ctx*/NULL, NID_key_usage, "critical,digitalSignature,keyEncipherment")) == NULL || !X509_add_ext(cert, ext, -1))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to add extension to X.509 certificate."), 1);
+ goto done;
+ }
+ }
+
+ if (!saw_ext_usage)
+ {
+ if ((ext = X509V3_EXT_conf_nid(/*conf*/NULL, /*ctx*/NULL, NID_ext_key_usage, tls_usage_strings[0])) == NULL || !X509_add_ext(cert, ext, -1))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to add extension to X.509 certificate."), 1);
+ goto done;
+ }
+ }
+
+ if (!saw_san)
+ {
+ if ((ext = openssl_create_san(common_name, /*num_alt_names*/0, /*alt_names*/NULL)) == NULL || !X509_add_ext(cert, ext, -1))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to add extension to X.509 certificate."), 1);
+ goto done;
+ }
+ }
+
+ // Try loading a root certificate...
+ http_make_path(root_crtfile, sizeof(root_crtfile), path, root_name ? root_name : "_site_", "crt");
+ http_make_path(root_keyfile, sizeof(root_keyfile), path, root_name ? root_name : "_site_", "key");
+
+ if (!access(root_crtfile, 0) && !access(root_keyfile, 0))
+ {
+ if ((bio = BIO_new_file(root_crtfile, "rb")) != NULL)
+ {
+ PEM_read_bio_X509(bio, &root_cert, /*cb*/NULL, /*u*/NULL);
+ BIO_free(bio);
+
+ if ((bio = BIO_new_file(root_keyfile, "rb")) != NULL)
+ {
+ PEM_read_bio_PrivateKey(bio, &root_key, /*cb*/NULL, /*u*/NULL);
+ BIO_free(bio);
+ }
+
+ if (!root_key)
+ {
+ // Only use root certificate if we have the key...
+ X509_free(root_cert);
+ root_cert = NULL;
+ }
+ }
+ }
+
+ if (!root_cert || !root_key)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to load X.509 CA certificate and private key."), 1);
+ goto done;
+ }
+
+ X509_set_issuer_name(cert, X509_get_subject_name(root_cert));
+ X509_sign(cert, root_key, EVP_sha256());
+
+ // Save the certificate...
+ http_make_path(crtfile, sizeof(crtfile), path, common_name, "crt");
+
+ if ((bio = BIO_new_file(crtfile, "wb")) == NULL)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(errno), 0);
+ goto done;
+ }
+
+ if (!PEM_write_bio_X509(bio, cert))
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to write X.509 certificate."), 1);
+ BIO_free(bio);
+ goto done;
+ }
+
+ PEM_write_bio_X509(bio, root_cert);
+
+ BIO_free(bio);
+ result = true;
+ DEBUG_puts("1cupsSignRequest: Successfully created credentials.");
+
+ // Cleanup...
+ done:
+
+ if (exts)
+ sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+ if (crq)
+ X509_REQ_free(crq);
+ if (cert)
+ X509_free(cert);
+ if (root_cert)
+ X509_free(root_cert);
+ if (root_key)
+ EVP_PKEY_free(root_key);
+
+ return (result);
}
-/*
- * 'httpCopyCredentials()' - Copy the credentials associated with the peer in
- * an encrypted connection.
- */
+//
+// 'httpCopyCredentials()' - Copy the credentials associated with the peer in
+// an encrypted connection.
+//
-int // O - Status of call (0 = success)
+bool // O - Status of call (`true` = success)
httpCopyCredentials(
http_t *http, // I - Connection to server
cups_array_t **credentials) // O - Array of credentials
@@ -351,7 +1037,7 @@ httpCopyCredentials(
*credentials = NULL;
if (!http || !http->tls || !credentials)
- return (-1);
+ return (false);
*credentials = cupsArrayNew(NULL, NULL, NULL, 0, NULL, NULL);
chain = SSL_get_peer_cert_chain(http->tls);
@@ -386,13 +1072,13 @@ httpCopyCredentials(
}
}
- return (0);
+ return (true);
}
-/*
- * '_httpCreateCredentials()' - Create credentials in the internal format.
- */
+//
+// '_httpCreateCredentials()' - Create credentials in the internal format.
+//
http_tls_credentials_t // O - Internal credentials
_httpCreateCredentials(
@@ -404,35 +1090,24 @@ _httpCreateCredentials(
}
-/*
- * '_httpFreeCredentials()' - Free internal credentials.
- */
-
-void
-_httpFreeCredentials(
- http_tls_credentials_t credentials) // I - Internal credentials
-{
- X509_free(credentials);
-}
-
-
-/*
- * 'httpCredentialsAreValidForName()' - Return whether the credentials are valid for the given name.
- */
+//
+// 'httpCredentialsAreValidForName()' - Return whether the credentials are valid
+// for the given name.
+//
-int // O - 1 if valid, 0 otherwise
+bool // O - `true` if valid, `false` otherwise
httpCredentialsAreValidForName(
cups_array_t *credentials, // I - Credentials
const char *common_name) // I - Name to check
{
X509 *cert; // Certificate
- int result = 0; // Result
+ bool result = false; // Result
- cert = http_create_credential((http_credential_t *)cupsArrayGetFirst(credentials));
+ cert = openssl_create_credential((http_credential_t *)cupsArrayGetFirst(credentials));
if (cert)
{
- result = X509_check_host(cert, common_name, strlen(common_name), 0, NULL);
+ result = X509_check_host(cert, common_name, strlen(common_name), 0, NULL) != 0;
X509_free(cert);
}
@@ -441,9 +1116,9 @@ httpCredentialsAreValidForName(
}
-/*
- * 'httpCredentialsGetTrust()' - Return the trust of credentials.
- */
+//
+// 'httpCredentialsGetTrust()' - Return the trust of credentials.
+//
http_trust_t // O - Level of trust
httpCredentialsGetTrust(
@@ -462,7 +1137,7 @@ httpCredentialsGetTrust(
return (HTTP_TRUST_UNKNOWN);
}
- if ((cert = http_create_credential((http_credential_t *)cupsArrayGetFirst(credentials))) == NULL)
+ if ((cert = openssl_create_credential((http_credential_t *)cupsArrayGetFirst(credentials))) == NULL)
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create credentials from array."), 1);
return (HTTP_TRUST_UNKNOWN);
@@ -471,7 +1146,7 @@ httpCredentialsGetTrust(
if (cg->any_root < 0)
{
_cupsSetDefaults();
-// http_load_crl();
+// openssl_load_crl();
}
// Look this common name up in the default keychains...
@@ -479,8 +1154,8 @@ httpCredentialsGetTrust(
if (tcreds)
{
- char credentials_str[1024], /* String for incoming credentials */
- tcreds_str[1024]; /* String for saved credentials */
+ char credentials_str[1024], // String for incoming credentials
+ tcreds_str[1024]; // String for saved credentials
httpCredentialsString(credentials, credentials_str, sizeof(credentials_str));
httpCredentialsString(tcreds, tcreds_str, sizeof(tcreds_str));
@@ -566,7 +1241,7 @@ httpCredentialsGetTrust(
time_t curtime; // Current date/time
time(&curtime);
- if (curtime < http_get_date(cert, 0) || curtime > http_get_date(cert, 1))
+ if (curtime < openssl_get_date(cert, 0) || curtime > openssl_get_date(cert, 1))
{
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Credentials have expired."), 1);
trust = HTTP_TRUST_EXPIRED;
@@ -585,9 +1260,9 @@ httpCredentialsGetTrust(
}
-/*
- * 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
- */
+//
+// 'httpCredentialsGetExpiration()' - Return the expiration date of the credentials.
+//
time_t // O - Expiration date of credentials
httpCredentialsGetExpiration(
@@ -597,9 +1272,9 @@ httpCredentialsGetExpiration(
X509 *cert; // Certificate
- if ((cert = http_create_credential((http_credential_t *)cupsArrayGetFirst(credentials))) != NULL)
+ if ((cert = openssl_create_credential((http_credential_t *)cupsArrayGetFirst(credentials))) != NULL)
{
- result = http_get_date(cert, 1);
+ result = openssl_get_date(cert, 1);
X509_free(cert);
}
@@ -607,9 +1282,9 @@ httpCredentialsGetExpiration(
}
-/*
- * 'httpCredentialsString()' - Return a string representing the credentials.
- */
+//
+// 'httpCredentialsString()' - Return a string representing the credentials.
+//
size_t // O - Total size of credentials string
httpCredentialsString(
@@ -630,7 +1305,7 @@ httpCredentialsString(
*buffer = '\0';
first = (http_credential_t *)cupsArrayGetFirst(credentials);
- cert = http_create_credential(first);
+ cert = openssl_create_credential(first);
if (cert)
{
@@ -644,7 +1319,7 @@ httpCredentialsString(
X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, name, sizeof(name));
X509_NAME_get_text_by_NID(X509_get_issuer_name(cert), NID_commonName, issuer, sizeof(issuer));
- expiration = http_get_date(cert, 1);
+ expiration = openssl_get_date(cert, 1);
switch (X509_get_signature_nid(cert))
{
@@ -695,196 +1370,21 @@ httpCredentialsString(
}
-/*
- * 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
- */
-
-bool // O - `true` on success, `false` on error
-httpLoadCredentials(
- const char *path, // I - Keychain/PKCS#12 path
- cups_array_t **credentials, // IO - Credentials
- const char *common_name) // I - Common name for credentials
-{
- cups_file_t *fp; // Certificate file
- char filename[1024], // filename.crt
- temp[1024], // Temporary string
- line[256]; // Base64-encoded line
- unsigned char *data = NULL; // Buffer for cert data
- size_t alloc_data = 0, // Bytes allocated
- num_data = 0, // Bytes used
- decoded; // Bytes decoded
- int in_certificate = 0;
- // In a certificate?
-
-
- if (credentials)
- *credentials = NULL;
-
- if (!credentials || !common_name)
- return (false);
-
- if (!path)
- path = http_default_path(temp, sizeof(temp));
- if (!path)
- return (false);
-
- http_make_path(filename, sizeof(filename), path, common_name, "crt");
-
- if ((fp = cupsFileOpen(filename, "r")) == NULL)
- return (false);
-
- while (cupsFileGets(fp, line, sizeof(line)))
- {
- if (!strcmp(line, "-----BEGIN CERTIFICATE-----"))
- {
- if (in_certificate)
- {
- /*
- * Missing END CERTIFICATE...
- */
-
- httpFreeCredentials(*credentials);
- *credentials = NULL;
- break;
- }
-
- in_certificate = 1;
- }
- else if (!strcmp(line, "-----END CERTIFICATE-----"))
- {
- if (!in_certificate || !num_data)
- {
- /*
- * Missing data...
- */
-
- httpFreeCredentials(*credentials);
- *credentials = NULL;
- break;
- }
-
- if (!*credentials)
- *credentials = cupsArrayNew(NULL, NULL, NULL, 0, NULL, NULL);
-
- if (httpAddCredential(*credentials, data, num_data))
- {
- httpFreeCredentials(*credentials);
- *credentials = NULL;
- break;
- }
-
- num_data = 0;
- in_certificate = 0;
- }
- else if (in_certificate)
- {
- if (alloc_data == 0)
- {
- data = malloc(2048);
- alloc_data = 2048;
-
- if (!data)
- break;
- }
- else if ((num_data + strlen(line)) >= alloc_data)
- {
- unsigned char *tdata = realloc(data, alloc_data + 1024);
- /* Expanded buffer */
-
- if (!tdata)
- {
- httpFreeCredentials(*credentials);
- *credentials = NULL;
- break;
- }
-
- data = tdata;
- alloc_data += 1024;
- }
-
- decoded = alloc_data - num_data;
- httpDecode64((char *)data + num_data, &decoded, line, NULL);
- num_data += (size_t)decoded;
- }
- }
-
- cupsFileClose(fp);
-
- if (in_certificate)
- {
- /*
- * Missing END CERTIFICATE...
- */
-
- httpFreeCredentials(*credentials);
- *credentials = NULL;
- }
-
- if (data)
- free(data);
-
- return (*credentials != NULL);
-}
-
-
-/*
- * 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
- */
+//
+// '_httpFreeCredentials()' - Free internal credentials.
+//
-bool // O - `true` on success, `false` on error
-httpSaveCredentials(
- const char *path, // I - Keychain/PKCS#12 path
- cups_array_t *credentials, // I - Credentials
- const char *common_name) // I - Common name for credentials
+void
+_httpFreeCredentials(
+ http_tls_credentials_t credentials) // I - Internal credentials
{
- cups_file_t *fp; // Certificate file
- char filename[1024], // filename.crt
- nfilename[1024],// filename.crt.N
- temp[1024], // Temporary string
- line[256]; // Base64-encoded line
- const unsigned char *ptr; // Pointer into certificate
- ssize_t remaining; // Bytes left
- http_credential_t *cred; // Current credential
-
-
- if (!credentials || !common_name)
- return (false);
-
- if (!path)
- path = http_default_path(temp, sizeof(temp));
- if (!path)
- return (false);
-
- http_make_path(filename, sizeof(filename), path, common_name, "crt");
- snprintf(nfilename, sizeof(nfilename), "%s.N", filename);
-
- if ((fp = cupsFileOpen(nfilename, "w")) == NULL)
- return (false);
-
-#ifndef _WIN32
- fchmod(cupsFileNumber(fp), 0600);
-#endif // !_WIN32
-
- for (cred = (http_credential_t *)cupsArrayGetFirst(credentials); cred; cred = (http_credential_t *)cupsArrayGetNext(credentials))
- {
- cupsFilePuts(fp, "-----BEGIN CERTIFICATE-----\n");
- for (ptr = cred->data, remaining = (ssize_t)cred->datalen; remaining > 0; remaining -= 45, ptr += 45)
- {
- httpEncode64(line, sizeof(line), (char *)ptr, remaining > 45 ? 45 : (size_t)remaining, false);
- cupsFilePrintf(fp, "%s\n", line);
- }
- cupsFilePuts(fp, "-----END CERTIFICATE-----\n");
- }
-
- cupsFileClose(fp);
-
- return (!rename(nfilename, filename));
+ X509_free(credentials);
}
-/*
- * '_httpTLSInitialize()' - Initialize the TLS stack.
- */
+//
+// '_httpTLSInitialize()' - Initialize the TLS stack.
+//
void
_httpTLSInitialize(void)
@@ -893,9 +1393,9 @@ _httpTLSInitialize(void)
}
-/*
- * '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
- */
+//
+// '_httpTLSPending()' - Return the number of pending TLS-encrypted bytes.
+//
size_t // O - Bytes available
_httpTLSPending(http_t *http) // I - HTTP connection
@@ -904,9 +1404,9 @@ _httpTLSPending(http_t *http) // I - HTTP connection
}
-/*
- * '_httpTLSRead()' - Read from a SSL/TLS connection.
- */
+//
+// '_httpTLSRead()' - Read from a SSL/TLS connection.
+//
int // O - Bytes read
_httpTLSRead(http_t *http, // I - Connection to server
@@ -922,29 +1422,11 @@ _httpTLSRead(http_t *http, // I - Connection to server
}
-/*
- * '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
- */
-
-void
-_httpTLSSetOptions(int options, // I - Options
- int min_version, // I - Minimum TLS version
- int max_version) // I - Maximum TLS version
-{
- if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
- {
- tls_options = options;
- tls_min_version = min_version;
- tls_max_version = max_version;
- }
-}
-
-
-/*
- * '_httpTLSStart()' - Set up SSL/TLS support on a connection.
- */
+//
+// '_httpTLSStart()' - Set up SSL/TLS support on a connection.
+//
-bool /* O - `true` on success, `false` on failure */
+bool // O - `true` on success, `false` on failure
_httpTLSStart(http_t *http) // I - Connection to server
{
BIO *bio; // Basic input/output context
@@ -1085,9 +1567,9 @@ _httpTLSStart(http_t *http) // I - Connection to server
{
DEBUG_printf(("4_httpTLSStart: Auto-create credentials for \"%s\".", cn));
- if (!cupsMakeServerCredentials(tls_keypath, cn, 0, NULL, time(NULL) + 3650 * 86400))
+ if (!cupsCreateCredentials(tls_keypath, false, CUPS_CREDPURPOSE_SERVER_AUTH, CUPS_CREDTYPE_DEFAULT, CUPS_CREDUSAGE_DEFAULT_TLS, NULL, NULL, NULL, NULL, NULL, cn, 0, NULL, NULL, time(NULL) + 3650 * 86400))
{
- DEBUG_puts("4_httpTLSStart: cupsMakeServerCredentials failed.");
+ DEBUG_puts("4_httpTLSStart: cupsCreateCredentials failed.");
http->error = errno = EINVAL;
http->status = HTTP_STATUS_ERROR;
_cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create server credentials."), 1);
@@ -1208,9 +1690,9 @@ _httpTLSStart(http_t *http) // I - Connection to server
}
-/*
- * '_httpTLSStop()' - Shut down SSL/TLS on a connection.
- */
+//
+// '_httpTLSStop()' - Shut down SSL/TLS on a connection.
+//
void
_httpTLSStop(http_t *http) // I - Connection to server
@@ -1228,9 +1710,9 @@ _httpTLSStop(http_t *http) // I - Connection to server
}
-/*
- * '_httpTLSWrite()' - Write to a SSL/TLS connection.
- */
+//
+// '_httpTLSWrite()' - Write to a SSL/TLS connection.
+//
int // O - Bytes written
_httpTLSWrite(http_t *http, // I - Connection to server
@@ -1241,9 +1723,9 @@ _httpTLSWrite(http_t *http, // I - Connection to server
}
-/*
- * 'http_bio_ctrl()' - Control the HTTP connection.
- */
+//
+// 'http_bio_ctrl()' - Control the HTTP connection.
+//
static long // O - Result/data
http_bio_ctrl(BIO *h, // I - BIO data
@@ -1285,9 +1767,9 @@ http_bio_ctrl(BIO *h, // I - BIO data
}
-/*
- * 'http_bio_free()' - Free OpenSSL data.
- */
+//
+// 'http_bio_free()' - Free OpenSSL data.
+//
static int // O - 1 on success, 0 on failure
http_bio_free(BIO *h) // I - BIO data
@@ -1304,9 +1786,9 @@ http_bio_free(BIO *h) // I - BIO data
}
-/*
- * 'http_bio_new()' - Initialize an OpenSSL BIO structure.
- */
+//
+// 'http_bio_new()' - Initialize an OpenSSL BIO structure.
+//
static int // O - 1 on success, 0 on failure
http_bio_new(BIO *h) // I - BIO data
@@ -1323,9 +1805,9 @@ http_bio_new(BIO *h) // I - BIO data
}
-/*
- * 'http_bio_puts()' - Send a string for OpenSSL.
- */
+//
+// 'http_bio_puts()' - Send a string for OpenSSL.
+//
static int // O - Bytes written
http_bio_puts(BIO *h, // I - BIO data
@@ -1341,9 +1823,9 @@ http_bio_puts(BIO *h, // I - BIO data
}
-/*
- * 'http_bio_read()' - Read data for OpenSSL.
- */
+//
+// 'http_bio_read()' - Read data for OpenSSL.
+//
static int // O - Bytes read
http_bio_read(BIO *h, // I - BIO data
@@ -1382,9 +1864,9 @@ http_bio_read(BIO *h, // I - BIO data
}
-/*
- * 'http_bio_write()' - Write data for OpenSSL.
- */
+//
+// 'http_bio_write()' - Write data for OpenSSL.
+//
static int // O - Bytes written
http_bio_write(BIO *h, // I - BIO data
@@ -1403,12 +1885,40 @@ http_bio_write(BIO *h, // I - BIO data
}
-/*
- * 'http_create_credential()' - Create a single credential in the internal format.
- */
+//
+// 'openssl_add_ext()' - Add an extension.
+//
+
+static bool // O - `true` on success, `false` on error
+openssl_add_ext(
+ STACK_OF(X509_EXTENSION) *exts, // I - Stack of extensions
+ int nid, // I - Extension ID
+ const char *value) // I - Value
+{
+ X509_EXTENSION *ext = NULL; // Extension
+
+
+ DEBUG_printf(("3openssl_add_ext(exts=%p, nid=%d, value=\"%s\")", (void *)exts, nid, value));
+
+ // Create and add the extension...
+ if ((ext = X509V3_EXT_conf_nid(/*conf*/NULL, /*ctx*/NULL, nid, value)) == NULL)
+ {
+ DEBUG_puts("4openssl_add_ext: Unable to create extension, returning false.");
+ return (false);
+ }
+
+ sk_X509_EXTENSION_push(exts, ext);
+
+ return (true);
+}
+
+
+//
+// 'openssl_create_credential()' - Create a single credential in the internal format.
+//
static X509 * // O - Certificate
-http_create_credential(
+openssl_create_credential(
http_credential_t *credential) // I - Credential
{
X509 *cert = NULL; // Certificate
@@ -1429,64 +1939,165 @@ http_create_credential(
}
-/*
- * 'http_default_path()' - Get the default credential store path.
- */
+//
+// 'openssl_create_key()' - Create a suitable key pair for a certificate/signing request.
+//
-static const char * // O - Path or NULL on error
-http_default_path(
- char *buffer, // I - Path buffer
- size_t bufsize) // I - Size of path buffer
+static EVP_PKEY * // O - Key pair
+openssl_create_key(
+ cups_credtype_t type) // I - Type of key
{
- _cups_globals_t *cg = _cupsGlobals();
- // Pointer to library globals
+ EVP_PKEY *pkey; // Key pair
+ EVP_PKEY_CTX *ctx; // Key generation context
+ int algid; // Algorithm NID
+ int bits = 0; // Bits
+ int curveid = 0; // Curve NID
- if (cg->userconfig)
+ switch (type)
{
- if (mkdir(cg->userconfig, 0755) && errno != EEXIST)
- {
- DEBUG_printf(("1http_default_path: Failed to make directory '%s': %s", cg->userconfig, strerror(errno)));
- return (NULL);
- }
+ case CUPS_CREDTYPE_ECDSA_P256_SHA256 :
+ algid = EVP_PKEY_EC;
+ curveid = NID_secp256k1;
+ break;
+
+ case CUPS_CREDTYPE_ECDSA_P384_SHA256 :
+ algid = EVP_PKEY_EC;
+ curveid = NID_secp384r1;
+ break;
+
+ case CUPS_CREDTYPE_ECDSA_P521_SHA256 :
+ algid = EVP_PKEY_EC;
+ curveid = NID_secp521r1;
+ break;
+
+ case CUPS_CREDTYPE_RSA_2048_SHA256 :
+ algid = EVP_PKEY_RSA;
+ bits = 2048;
+ break;
- snprintf(buffer, bufsize, "%s/ssl", cg->userconfig);
-
- if (mkdir(buffer, 0700) && errno != EEXIST)
- {
- DEBUG_printf(("1http_default_path: Failed to make directory '%s': %s", buffer, strerror(errno)));
- return (NULL);
- }
+ default :
+ case CUPS_CREDTYPE_RSA_3072_SHA256 :
+ algid = EVP_PKEY_RSA;
+ bits = 3072;
+ break;
+
+ case CUPS_CREDTYPE_RSA_4096_SHA256 :
+ algid = EVP_PKEY_RSA;
+ bits = 4096;
+ break;
}
+
+ pkey = NULL;
+
+ if ((ctx = EVP_PKEY_CTX_new_id(algid, NULL)) == NULL)
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create private key context."), 1);
+ else if (EVP_PKEY_keygen_init(ctx) <= 0)
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to initialize private key context."), 1);
+ else if (bits && EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) <= 0)
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to configure private key context."), 1);
+ else if (curveid && EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx, curveid) <= 0)
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to configure private key context."), 1);
+ else if (EVP_PKEY_keygen(ctx, &pkey) <= 0)
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, _("Unable to create private key."), 1);
+
+ EVP_PKEY_CTX_free(ctx);
+
+ return (pkey);
+}
+
+
+//
+// 'openssl_create_name()' - Create an X.509 name value for a certificate/signing request.
+//
+
+static X509_NAME * // O - X.509 name value
+openssl_create_name(
+ const char *organization, // I - Organization or `NULL` to use common name
+ const char *org_unit, // I - Organizational unit or `NULL` for none
+ const char *locality, // I - City/town or `NULL` for "Unknown"
+ const char *state_province, // I - State/province or `NULL` for "Unknown"
+ const char *country, // I - Country or `NULL` for locale-based default
+ const char *common_name) // I - Common name
+{
+ X509_NAME *name; // Subject/issuer name
+ cups_lang_t *language; // Default language info
+ const char *langname; // Language name
+
+
+ language = cupsLangDefault();
+ langname = cupsLangGetName(language);
+ name = X509_NAME_new();
+ if (country)
+ X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (unsigned char *)country, -1, -1, 0);
+ else if (strlen(langname) == 5)
+ X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (unsigned char *)langname + 3, -1, -1, 0);
else
+ X509_NAME_add_entry_by_txt(name, SN_countryName, MBSTRING_ASC, (unsigned char *)"US", -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_commonName, MBSTRING_ASC, (unsigned char *)common_name, -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_organizationName, MBSTRING_ASC, (unsigned char *)(organization ? organization : common_name), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_organizationalUnitName, MBSTRING_ASC, (unsigned char *)(org_unit ? org_unit : ""), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_stateOrProvinceName, MBSTRING_ASC, (unsigned char *)(state_province ? state_province : "Unknown"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, SN_localityName, MBSTRING_ASC, (unsigned char *)(locality ? locality : "Unknown"), -1, -1, 0);
+
+ return (name);
+}
+
+
+//
+// 'openssl_create_san()' - Create a list of subjectAltName values for a certificate/signing request.
+//
+
+static X509_EXTENSION * // O - Extension
+openssl_create_san(
+ const char *common_name, // I - Common name
+ size_t num_alt_names, // I - Number of alternate names
+ const char * const *alt_names) // I - List of alternate names
+{
+ char temp[2048], // Temporary string
+ *tempptr; // Pointer into temporary string
+ size_t i; // Looping var
+
+
+ // Add the common name
+ snprintf(temp, sizeof(temp), "DNS:%s", common_name);
+ tempptr = temp + strlen(temp);
+
+ if (strstr(common_name, ".local") == NULL)
{
- if (mkdir(cg->sysconfig, 0755) && errno != EEXIST)
- {
- DEBUG_printf(("1http_default_path: Failed to make directory '%s': %s", cg->sysconfig, strerror(errno)));
- return (NULL);
- }
+ // Add common_name.local to the list, too...
+ char localname[256], // hostname.local
+ *localptr; // Pointer into localname
+
+ cupsCopyString(localname, common_name, sizeof(localname));
+ if ((localptr = strchr(localname, '.')) != NULL)
+ *localptr = '\0';
- snprintf(buffer, bufsize, "%s/ssl", cg->sysconfig);
+ snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), ",DNS:%s.local", localname);
+ tempptr += strlen(tempptr);
+ }
- if (mkdir(buffer, 0700) && errno != EEXIST)
+ // Add any alternate names...
+ for (i = 0; i < num_alt_names; i ++)
+ {
+ if (strcmp(alt_names[i], "localhost"))
{
- DEBUG_printf(("1http_default_path: Failed to make directory '%s': %s", buffer, strerror(errno)));
- return (NULL);
+ snprintf(tempptr, sizeof(temp) - (size_t)(tempptr - temp), ",DNS:%s", alt_names[i]);
+ tempptr += strlen(tempptr);
}
}
- DEBUG_printf(("1http_default_path: Using default path \"%s\".", buffer));
-
- return (buffer);
+ // Return the stack
+ return (X509V3_EXT_conf_nid(/*conf*/NULL, /*ctx*/NULL, NID_subject_alt_name, temp));
}
//
-// 'http_get_date()' - Get the notBefore or notAfter date of a certificate.
+// 'openssl_get_date()' - Get the notBefore or notAfter date of a certificate.
//
static time_t // O - UNIX time in seconds
-http_get_date(X509 *cert, // I - Certificate
+openssl_get_date(X509 *cert, // I - Certificate
int which) // I - 0 for notBefore, 1 for notAfter
{
unsigned char *expiration; // Expiration date of cert
@@ -1527,12 +2138,12 @@ http_get_date(X509 *cert, // I - Certificate
#if 0
-/*
- * 'http_load_crl()' - Load the certificate revocation list, if any.
- */
+//
+// 'openssl_load_crl()' - Load the certificate revocation list, if any.
+//
static void
-http_load_crl(void)
+openssl_load_crl(void)
{
cupsMutexLock(&tls_mutex);
@@ -1621,98 +2232,3 @@ http_load_crl(void)
cupsMutexUnlock(&tls_mutex);
}
#endif // 0
-
-
-/*
- * 'http_make_path()' - Format a filename for a certificate or key file.
- */
-
-static const char * // O - Filename
-http_make_path(
- char *buffer, // I - Filename buffer
- size_t bufsize, // I - Size of buffer
- const char *dirname, // I - Directory
- const char *filename, // I - Filename (usually hostname)
- const char *ext) // I - Extension
-{
- char *bufptr, // Pointer into buffer
- *bufend = buffer + bufsize - 1; // End of buffer
-
-
- snprintf(buffer, bufsize, "%s/", dirname);
- bufptr = buffer + strlen(buffer);
-
- while (*filename && bufptr < bufend)
- {
- if (_cups_isalnum(*filename) || *filename == '-' || *filename == '.')
- *bufptr++ = *filename;
- else
- *bufptr++ = '_';
-
- filename ++;
- }
-
- if (bufptr < bufend && filename[-1] != '.')
- *bufptr++ = '.';
-
- cupsCopyString(bufptr, ext, (size_t)(bufend - bufptr + 1));
-
- return (buffer);
-}
-
-
-//
-// 'http_x509_add_ext()' - Add an extension to a certificate.
-//
-
-static bool
-http_x509_add_ext(X509 *cert, // I - Certificate
- int nid, // I - Extension ID
- const char *value) // I - Value
-{
- bool ret; // Return value
- X509_EXTENSION *ex = NULL; // Extension
- X509V3_CTX ctx; // Certificate context
-
-
- DEBUG_printf(("3http_x509_add_ext(cert=%p, nid=%d, value=\"%s\")", (void *)cert, nid, value));
-
- // Don't use a configuration database...
- X509V3_set_ctx_nodb(&ctx);
-
- // Self-signed certificates use the same issuer and subject...
- X509V3_set_ctx(&ctx, /*issuer*/cert, /*subject*/cert, /*req*/NULL, /*crl*/NULL, /*flags*/0);
-
- // Create and add the extension...
- if ((ex = X509V3_EXT_conf_nid(/*conf*/NULL, &ctx, nid, value)) == NULL)
- {
- DEBUG_puts("4http_x509_add_ext: Unable to create extension, returning false.");
- return (false);
- }
-
- ret = X509_add_ext(cert, ex, -1) != 0;
-
- DEBUG_printf(("4http_x509_add_ext: X509_add_ext returned %s.", ret ? "true" : "false"));
-
- // Free the extension and return...
- X509_EXTENSION_free(ex);
-
- return (ret);
-}
-
-
-//
-// 'http_x509_add_san()' - Add a subjectAltName extension to an X.509 certificate.
-//
-
-static void
-http_x509_add_san(X509 *cert, // I - Certificate
- const char *name) // I - Hostname
-{
- char dns_name[1024]; // DNS: prefixed hostname
-
-
- // The subjectAltName value for DNS names starts with a DNS: prefix...
- snprintf(dns_name, sizeof(dns_name), "DNS:%s", name);
- http_x509_add_ext(cert, NID_subject_alt_name, dns_name);
-}
diff --git a/cups/tls.c b/cups/tls.c
index 165d55874..bd84d2a37 100644
--- a/cups/tls.c
+++ b/cups/tls.c
@@ -1,22 +1,19 @@
-/*
- * TLS routines for CUPS.
- *
- * Copyright © 2021-2022 by OpenPrinting.
- * Copyright @ 2007-2014 by Apple Inc.
- * Copyright @ 1997-2007 by Easy Software Products, all rights reserved.
- *
- * Licensed under Apache License v2.0. See the file "LICENSE" for more
- * information.
- */
-
-/*
- * Include necessary headers...
- */
+//
+// TLS routines for CUPS.
+//
+// Copyright © 2021-2023 by OpenPrinting.
+// Copyright @ 2007-2014 by Apple Inc.
+// Copyright @ 1997-2007 by Easy Software Products, all rights reserved.
+//
+// Licensed under Apache License v2.0. See the file "LICENSE" for more
+// information.
+//
#include "cups-private.h"
#include "debug-internal.h"
#include
#include
+#include
#ifdef _WIN32
# include
#else
@@ -24,17 +21,580 @@
# include
# include
# include
-#endif /* _WIN32 */
+#endif // _WIN32
-/*
- * Include platform-specific TLS code...
- */
+//
+// Local globals...
+//
-#ifdef HAVE_TLS
-# ifdef HAVE_OPENSSL
+static bool tls_auto_create = false;
+ // Auto-create self-signed certs?
+static char *tls_common_name = NULL;
+ // Default common name
+static char *tls_keypath = NULL;
+ // Certificate store path
+static cups_mutex_t tls_mutex = CUPS_MUTEX_INITIALIZER;
+ // Mutex for certificates
+static int tls_options = -1,// Options for TLS connections
+ tls_min_version = _HTTP_TLS_1_2,
+ tls_max_version = _HTTP_TLS_MAX;
+
+
+//
+// Local functions...
+//
+
+static char *http_copy_file(const char *path, const char *common_name, const char *ext);
+static const char *http_default_path(char *buffer, size_t bufsize);
+static bool http_default_san_cb(const char *common_name, const char *subject_alt_name, void *data);
+static const char *http_make_path(char *buffer, size_t bufsize, const char *dirname, const char *filename, const char *ext);
+static bool http_save_file(const char *path, const char *common_name, const char *ext, const char *value);
+
+
+//
+// Include platform-specific TLS code...
+//
+
+#ifdef HAVE_OPENSSL
# include "tls-openssl.c"
-# elif defined(HAVE_GNUTLS)
-# include "tls-gnutls.c"
-# endif /* HAVE_OPENSSL */
-#endif /* HAVE_TLS */
+#else // HAVE_GNUTLS
+# include "tls-gnutls.c"
+#endif /* HAVE_OPENSSL */
+
+
+//
+// 'cupsCopyCredentials()' - Copy the X.509 certificate chain to a string.
+//
+
+char *
+cupsCopyCredentials(
+ const char *path, // I - Directory path for certificate/key store or `NULL` for default
+ const char *common_name) // I - Common name
+{
+ return (http_copy_file(path, common_name, "crt"));
+}
+
+
+//
+// 'cupsCopyCredentialsKey()' - Copy the private key to a string.
+//
+
+char *
+cupsCopyCredentialsKey(
+ const char *path, // I - Directory path for certificate/key store or `NULL` for default
+ const char *common_name) // I - Common name
+{
+ return (http_copy_file(path, common_name, "key"));
+}
+
+
+//
+// 'cupsCopyCredentialsRequest()' - Copy the X.509 certificate signing request to a string.
+//
+
+char *
+cupsCopyCredentialsRequest(
+ const char *path, // I - Directory path for certificate/key store or `NULL` for default
+ const char *common_name) // I - Common name
+{
+ return (http_copy_file(path, common_name, "csr"));
+}
+
+
+//
+// 'cupsSaveServerCredentials()' - Save the X.509 certificate chain associated
+// with a server.
+//
+// This function saves the the PEM-encoded X.509 certificate chain string to
+// the directory "path" or, if "path" is `NULL`, in a per-user or system-wide
+// (when running as root) certificate/key store. The "common_name" value must
+// match the value supplied when the @link cupsMakeServerRequest@ function was
+// called to obtain the certificate signing request (CSR). The saved
+// certificate is paired with the private key that was generated for the CSR,
+// allowing it to be used for encryption and signing.
+//
+
+extern bool // O - `true` on success, `false` on failure
+cupsSaveCredentials(
+ const char *path, // I - Directory path for certificate/key store or `NULL` for default
+ const char *common_name, // I - Common name for certificate
+ const char *credentials, // I - PEM-encoded certificate chain
+ const char *key) // I - PEM-encoded private key or `NULL` for none
+{
+ if (http_save_file(path, common_name, "crt", credentials))
+ {
+ if (key)
+ return (http_save_file(path, common_name, "key", key));
+ else
+ return (true);
+ }
+
+ return (false);
+}
+
+
+//
+// 'cupsSetServerCredentials()' - Set the default server credentials.
+//
+// Note: The server credentials are used by all threads in the running process.
+// This function is threadsafe.
+//
+
+bool // O - `true` on success, `false` on failure
+cupsSetServerCredentials(
+ const char *path, // I - Directory path for certificate/key store or `NULL` for default
+ const char *common_name, // I - Default common name for server
+ bool auto_create) // I - `true` = automatically create self-signed certificates
+{
+ char temp[1024]; // Default path buffer
+
+
+ DEBUG_printf(("cupsSetServerCredentials(path=\"%s\", common_name=\"%s\", auto_create=%d)", path, common_name, auto_create));
+
+ /*
+ * Use defaults as needed...
+ */
+
+ if (!path)
+ path = http_default_path(temp, sizeof(temp));
+
+ /*
+ * Range check input...
+ */
+
+ if (!path || !common_name)
+ {
+ _cupsSetError(IPP_STATUS_ERROR_INTERNAL, strerror(EINVAL), 0);
+ return (0);
+ }
+
+ cupsMutexLock(&tls_mutex);
+
+ /*
+ * Free old values...
+ */
+
+ if (tls_keypath)
+ _cupsStrFree(tls_keypath);
+
+ if (tls_common_name)
+ _cupsStrFree(tls_common_name);
+
+ /*
+ * Save the new values...
+ */
+
+ tls_keypath = _cupsStrAlloc(path);
+ tls_auto_create = auto_create;
+ tls_common_name = _cupsStrAlloc(common_name);
+
+ cupsMutexUnlock(&tls_mutex);
+
+ return (1);
+}
+
+
+//
+// 'httpLoadCredentials()' - Load X.509 credentials from a keychain file.
+//
+
+bool // O - `true` on success, `false` on error
+httpLoadCredentials(
+ const char *path, // I - Keychain/PKCS#12 path
+ cups_array_t **credentials, // IO - Credentials
+ const char *common_name) // I - Common name for credentials
+{
+ cups_file_t *fp; // Certificate file
+ char filename[1024], // filename.crt
+ temp[1024], // Temporary string
+ line[256]; // Base64-encoded line
+ unsigned char *data = NULL; // Buffer for cert data
+ size_t alloc_data = 0, // Bytes allocated
+ num_data = 0, // Bytes used
+ decoded; // Bytes decoded
+ bool in_certificate = false;
+ // In a certificate?
+
+
+ if (credentials)
+ *credentials = NULL;
+
+ if (!credentials || !common_name)
+ return (false);
+
+ if (!path)
+ path = http_default_path(temp, sizeof(temp));
+ if (!path)
+ return (false);
+
+ http_make_path(filename, sizeof(filename), path, common_name, "crt");
+
+ if ((fp = cupsFileOpen(filename, "r")) == NULL)
+ return (false);
+
+ while (cupsFileGets(fp, line, sizeof(line)))
+ {
+ if (!strcmp(line, "-----BEGIN CERTIFICATE-----"))
+ {
+ if (in_certificate)
+ {
+ // Missing END CERTIFICATE...
+ httpFreeCredentials(*credentials);
+ *credentials = NULL;
+ break;
+ }
+
+ in_certificate = true;
+ }
+ else if (!strcmp(line, "-----END CERTIFICATE-----"))
+ {
+ if (!in_certificate || !num_data)
+ {
+ // Missing data...
+ httpFreeCredentials(*credentials);
+ *credentials = NULL;
+ break;
+ }
+
+ if (!*credentials)
+ *credentials = cupsArrayNew(NULL, NULL, NULL, 0, NULL, NULL);
+
+ if (httpAddCredential(*credentials, data, num_data))
+ {
+ httpFreeCredentials(*credentials);
+ *credentials = NULL;
+ break;
+ }
+
+ num_data = 0;
+ in_certificate = false;
+ }
+ else if (in_certificate)
+ {
+ if (alloc_data == 0)
+ {
+ data = malloc(2048);
+ alloc_data = 2048;
+
+ if (!data)
+ break;
+ }
+ else if ((num_data + strlen(line)) >= alloc_data)
+ {
+ unsigned char *tdata = realloc(data, alloc_data + 1024);
+ // Expanded buffer
+
+ if (!tdata)
+ {
+ httpFreeCredentials(*credentials);
+ *credentials = NULL;
+ break;
+ }
+
+ data = tdata;
+ alloc_data += 1024;
+ }
+
+ decoded = alloc_data - num_data;
+ httpDecode64((char *)data + num_data, &decoded, line, NULL);
+ num_data += (size_t)decoded;
+ }
+ }
+
+ cupsFileClose(fp);
+
+ if (in_certificate)
+ {
+ // Missing END CERTIFICATE...
+ httpFreeCredentials(*credentials);
+ *credentials = NULL;
+ }
+
+ if (data)
+ free(data);
+
+ return (*credentials != NULL);
+}
+
+
+//
+// 'httpSaveCredentials()' - Save X.509 credentials to a keychain file.
+//
+
+bool // O - `true` on success, `false` on error
+httpSaveCredentials(
+ const char *path, // I - Keychain/PKCS#12 path
+ cups_array_t *credentials, // I - Credentials
+ const char *common_name) // I - Common name for credentials
+{
+ cups_file_t *fp; // Certificate file
+ char filename[1024], // filename.crt
+ nfilename[1024],// filename.crt.N
+ temp[1024], // Temporary string
+ line[256]; // Base64-encoded line
+ const unsigned char *ptr; // Pointer into certificate
+ ssize_t remaining; // Bytes left
+ http_credential_t *cred; // Current credential
+
+
+ if (!credentials || !common_name)
+ return (false);
+
+ if (!path)
+ path = http_default_path(temp, sizeof(temp));
+ if (!path)
+ return (false);
+
+ http_make_path(filename, sizeof(filename), path, common_name, "crt");
+ snprintf(nfilename, sizeof(nfilename), "%s.N", filename);
+
+ if ((fp = cupsFileOpen(nfilename, "w")) == NULL)
+ return (false);
+
+#ifndef _WIN32
+ fchmod(cupsFileNumber(fp), 0600);
+#endif // !_WIN32
+
+ for (cred = (http_credential_t *)cupsArrayGetFirst(credentials); cred; cred = (http_credential_t *)cupsArrayGetNext(credentials))
+ {
+ cupsFilePuts(fp, "-----BEGIN CERTIFICATE-----\n");
+ for (ptr = cred->data, remaining = (ssize_t)cred->datalen; remaining > 0; remaining -= 45, ptr += 45)
+ {
+ httpEncode64(line, sizeof(line), (char *)ptr, remaining > 45 ? 45 : (size_t)remaining, false);
+ cupsFilePrintf(fp, "%s\n", line);
+ }
+ cupsFilePuts(fp, "-----END CERTIFICATE-----\n");
+ }
+
+ cupsFileClose(fp);
+
+ return (!rename(nfilename, filename));
+}
+
+
+//
+// '_httpTLSSetOptions()' - Set TLS protocol and cipher suite options.
+//
+
+void
+_httpTLSSetOptions(int options, // I - Options
+ int min_version, // I - Minimum TLS version
+ int max_version) // I - Maximum TLS version
+{
+ if (!(options & _HTTP_TLS_SET_DEFAULT) || tls_options < 0)
+ {
+ tls_options = options;
+ tls_min_version = min_version;
+ tls_max_version = max_version;
+ }
+}
+
+
+//
+// 'http_copy_file()' - Copy the contents of a file to a string.
+//
+
+static char *
+http_copy_file(const char *path, // I - Directory
+ const char *common_name, // I - Common name
+ const char *ext) // I - Extension
+{
+ char *s = NULL; // String
+ int fd; // File descriptor
+ char defpath[1024], // Default path
+ filename[1024]; // Filename
+ struct stat fileinfo; // File information
+
+
+ if (!common_name)
+ return (NULL);
+
+ if (!path)
+ path = http_default_path(defpath, sizeof(defpath));
+
+ if ((fd = open(http_make_path(filename, sizeof(filename), path, common_name, ext), O_RDONLY)) < 0)
+ return (NULL);
+
+ if (fstat(fd, &fileinfo))
+ goto done;
+
+ if (fileinfo.st_size > 65536)
+ {
+ close(fd);
+ return (NULL);
+ }
+
+ if ((s = calloc(1, (size_t)fileinfo.st_size + 1)) == NULL)
+ {
+ close(fd);
+ return (NULL);
+ }
+
+ if (read(fd, s, (size_t)fileinfo.st_size) < 0)
+ {
+ free(s);
+ s = NULL;
+ }
+
+ done:
+
+ close(fd);
+
+ return (s);
+}
+
+
+//
+// 'http_default_path()' - Get the default credential store path.
+//
+
+static const char * // O - Path or NULL on error
+http_default_path(
+ char *buffer, // I - Path buffer
+ size_t bufsize) // I - Size of path buffer
+{
+ _cups_globals_t *cg = _cupsGlobals();
+ // Pointer to library globals
+
+
+ if (cg->userconfig)
+ {
+ if (mkdir(cg->userconfig, 0755) && errno != EEXIST)
+ {
+ DEBUG_printf(("1http_default_path: Failed to make directory '%s': %s", cg->userconfig, strerror(errno)));
+ return (NULL);
+ }
+
+ snprintf(buffer, bufsize, "%s/ssl", cg->userconfig);
+
+ if (mkdir(buffer, 0700) && errno != EEXIST)
+ {
+ DEBUG_printf(("1http_default_path: Failed to make directory '%s': %s", buffer, strerror(errno)));
+ return (NULL);
+ }
+ }
+ else
+ {
+ if (mkdir(cg->sysconfig, 0755) && errno != EEXIST)
+ {
+ DEBUG_printf(("1http_default_path: Failed to make directory '%s': %s", cg->sysconfig, strerror(errno)));
+ return (NULL);
+ }
+
+ snprintf(buffer, bufsize, "%s/ssl", cg->sysconfig);
+
+ if (mkdir(buffer, 0700) && errno != EEXIST)
+ {
+ DEBUG_printf(("1http_default_path: Failed to make directory '%s': %s", buffer, strerror(errno)));
+ return (NULL);
+ }
+ }
+
+ DEBUG_printf(("1http_default_path: Using default path \"%s\".", buffer));
+
+ return (buffer);
+}
+
+
+//
+// 'http_default_san_cb()' - Validate a subjectAltName value.
+//
+
+static bool // O - `true` if OK, `false` otherwise
+http_default_san_cb(
+ const char *common_name, // I - Common name value
+ const char *subject_alt_name, // I - subjectAltName value
+ void *data) // I - Callback data (unused)
+{
+ size_t common_len; // Common name length
+
+
+ (void)data;
+
+ if (!_cups_strcasecmp(subject_alt_name, common_name) || !_cups_strcasecmp(subject_alt_name, "localhost"))
+ return (true);
+
+ common_len = strlen(common_name);
+
+ return (!_cups_strncasecmp(subject_alt_name, common_name, common_len) && subject_alt_name[common_len] == '.');
+}
+
+
+//
+// 'http_make_path()' - Format a filename for a certificate or key file.
+//
+
+static const char * // O - Filename
+http_make_path(
+ char *buffer, // I - Filename buffer
+ size_t bufsize, // I - Size of buffer
+ const char *dirname, // I - Directory
+ const char *filename, // I - Filename (usually hostname)
+ const char *ext) // I - Extension
+{
+ char *bufptr, // Pointer into buffer
+ *bufend = buffer + bufsize - 1; // End of buffer
+
+
+ snprintf(buffer, bufsize, "%s/", dirname);
+ bufptr = buffer + strlen(buffer);
+
+ while (*filename && bufptr < bufend)
+ {
+ if (_cups_isalnum(*filename) || *filename == '-' || *filename == '.')
+ *bufptr++ = *filename;
+ else
+ *bufptr++ = '_';
+
+ filename ++;
+ }
+
+ if (bufptr < bufend && filename[-1] != '.')
+ *bufptr++ = '.';
+
+ cupsCopyString(bufptr, ext, (size_t)(bufend - bufptr + 1));
+
+ return (buffer);
+}
+
+
+//
+// 'http_save_file()' - Save a string to a file.
+//
+
+static bool // O - `true` on success, `false` on failure
+http_save_file(const char *path, // I - Directory path for certificate/key store or `NULL` for default
+ const char *common_name, // I - Common name
+ const char *ext, // I - Extension
+ const char *value) // I - String value
+{
+ char defpath[1024], // Default path
+ filename[1024]; // Output filename
+ int fd; // File descriptor
+
+
+ // Range check input...
+ if (!common_name || !value)
+ return (false);
+
+ // Get default path as needed...
+ if (!path)
+ path = http_default_path(defpath, sizeof(defpath));
+
+ if ((fd = open(http_make_path(filename, sizeof(filename), path, common_name, ext), O_CREAT | O_WRONLY | O_TRUNC, 0644)) < 0)
+ return (false);
+
+ if (write(fd, value, strlen(value)) < 0)
+ {
+ close(fd);
+ unlink(filename);
+ return (false);
+ }
+
+ close(fd);
+
+ return (true);
+}
+
+
diff --git a/doc/Makefile b/doc/Makefile
index 495a2ad8a..5d00c7da5 100644
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -37,12 +37,19 @@ libs:
#
-# Make unit tests...
+# Run unit tests...
#
test:
+#
+# Make unit tests...
+#
+
+unittests:
+
+
#
# Remove all generated files...
#
diff --git a/doc/cupspm.epub b/doc/cupspm.epub
index 78381005b..f1fd12755 100644
Binary files a/doc/cupspm.epub and b/doc/cupspm.epub differ
diff --git a/doc/cupspm.html b/doc/cupspm.html
index 24bf5c9ba..a221dcd15 100644
--- a/doc/cupspm.html
+++ b/doc/cupspm.html
@@ -314,10 +314,15 @@ Contents
cupsCondInit
cupsCondWait
cupsConnectDest
+cupsCopyCredentials
+cupsCopyCredentialsKey
+cupsCopyCredentialsRequest
cupsCopyDest
cupsCopyDestConflicts
cupsCopyDestInfo
cupsCopyString
+cupsCreateCredentials
+cupsCreateCredentialsRequest
cupsCreateDestJob
cupsDNSSDAssembleFullName
cupsDNSSDBrowseDelete
@@ -414,9 +419,12 @@ Contents
cupsGetServer
cupsGetUser
cupsGetUserAgent
+cupsHMACData
cupsHashData
cupsHashString
cupsJSONDelete
+cupsJSONExportFile
+cupsJSONExportString
cupsJSONFind
cupsJSONGetChild
cupsJSONGetCount
@@ -426,14 +434,30 @@ Contents
cupsJSONGetSibling
cupsJSONGetString
cupsJSONGetType
-cupsJSONLoadFile
-cupsJSONLoadString
+cupsJSONImportFile
+cupsJSONImportString
+cupsJSONImportURL
cupsJSONNew
cupsJSONNewKey
cupsJSONNewNumber
cupsJSONNewString
-cupsJSONSaveFile
-cupsJSONSaveString
+cupsJWTDelete
+cupsJWTExportString
+cupsJWTGetAlgorithm
+cupsJWTGetClaimNumber
+cupsJWTGetClaimString
+cupsJWTGetClaimType
+cupsJWTGetClaimValue
+cupsJWTGetClaims
+cupsJWTHasValidSignature
+cupsJWTImportString
+cupsJWTMakePrivateKey
+cupsJWTMakePublicKey
+cupsJWTNew
+cupsJWTSetClaimNumber
+cupsJWTSetClaimString
+cupsJWTSetClaimValue
+cupsJWTSign
cupsLangAddStrings
cupsLangDefault
cupsLangFind
@@ -487,8 +511,10 @@ Contents
cupsSetPasswordCB
cupsSetServer
cupsSetServerCertCB
+cupsSetServerCredentials
cupsSetUser
cupsSetUserAgent
+cupsSignCredentialsRequest
cupsStartDestDocument
cupsTempFd
cupsTempFile
@@ -526,7 +552,12 @@ Contents
httpClose
httpCompareCredentials
httpConnect
+httpCopyCredentials
httpCreateCredentials
+httpCredentialsAreValidForName
+httpCredentialsGetExpiration
+httpCredentialsGetTrust
+httpCredentialsString
httpDecode64
httpEncode64
httpError
@@ -560,12 +591,14 @@ Contents
httpInitialize
httpIsChunked
httpIsEncrypted
+httpLoadCredentials
httpPeek
httpRead
httpReadRequest
httpReconnect
httpResolveHostname
httpResolveURI
+httpSaveCredentials
httpSeparateURI
httpSetAuthString
httpSetBlocking
@@ -718,12 +751,17 @@ Contents
cups_array_cb_t
cups_array_t
cups_bool_t
+cups_cert_san_cb_t
cups_client_cert_cb_t
cups_cond_t
+cups_credpurpose_t
+cups_credtype_t
+cups_credusage_t
cups_cspace_t
cups_cut_t
cups_dentry_t
cups_dest_cb_t
+cups_dest_flags_t
cups_dest_t
cups_dinfo_t
cups_dir_t
@@ -745,7 +783,10 @@ Contents
cups_jog_t
cups_json_t
cups_jtype_t
+cups_jwa_t
+cups_jwt_t
cups_lang_t
+cups_media_flags_t
cups_mediapos_t
cups_mutex_t
cups_oauth_cb_t
@@ -813,14 +854,20 @@ Contents
Enumerations
- cups_adv_e
- cups_bool_e
+- cups_credpurpose_e
+- cups_credtype_e
+- cups_credusage_e
- cups_cspace_e
- cups_cut_e
+- cups_dest_flags_e
- cups_dnssd_flags_e
- cups_dnssd_rrtype_e
- cups_edge_e
- cups_encoding_e
- cups_jog_e
- cups_jtype_e
+- cups_jwa_e
+- cups_media_flags_e
- cups_mediapos_e
- cups_order_e
- cups_orient_e
@@ -2470,6 +2517,45 @@ Discussion
The caller can pass CUPS_DEST_FLAGS_DEVICE for the "flags" argument to
connect directly to the device associated with the destination. Otherwise,
the connection is made to the CUPS scheduler associated with the destination.
+
+
+
+char *cupsCopyCredentials(const char *path, const char *common_name);
+Parameters
+
+| path |
+Directory path for certificate/key store or NULL for default |
+| common_name |
+Common name |
+
+Return Value
+Copy the X.509 certificate chain to a string.
+
+
+
+char *cupsCopyCredentialsKey(const char *path, const char *common_name);
+Parameters
+
+| path |
+Directory path for certificate/key store or NULL for default |
+| common_name |
+Common name |
+
+Return Value
+Copy the private key to a string.
+
+
+
+char *cupsCopyCredentialsRequest(const char *path, const char *common_name);
+Parameters
+
+| path |
+Directory path for certificate/key store or NULL for default |
+| common_name |
+Common name |
+
+Return Value
+Copy the X.509 certificate signing request to a string.
Copy a destination.
@@ -2568,6 +2654,238 @@
Parameters
Return Value
Length of string
+
+Make an X.509 certificate and private key pair.
+
+bool cupsCreateCredentials(const char *path, bool ca_cert, cups_credpurpose_t purpose, cups_credtype_t type, cups_credusage_t usage, const char *organization, const char *org_unit, const char *locality, const char *state_province, const char *country, const char *common_name, size_t num_alt_names, const char *const *alt_names, const char *root_name, time_t expiration_date);
+Parameters
+
+| path |
+Directory path for certificate/key store or NULL for default |
+| ca_cert |
+true to create a CA certificate, false for a client/server certificate |
+| purpose |
+Credential purposes |
+| type |
+Credential type |
+| usage |
+Credential usages |
+| organization |
+Organization or NULL to use common name |
+| org_unit |
+Organizational unit or NULL for none |
+| locality |
+City/town or NULL for "Unknown" |
+| state_province |
+State/province or NULL for "Unknown" |
+| country |
+Country or NULL for locale-based default |
+| common_name |
+Common name |
+| num_alt_names |
+Number of subject alternate names |
+| alt_names |
+Subject Alternate Names |
+| root_name |
+Root certificate/domain name or NULL for site/self-signed |
+| expiration_date |
+Expiration date |
+
+Return Value
+true on success, false on failure
+Discussion
+This function creates an X.509 certificate and private key pair. The
+certificate and key are stored in the directory "path" or, if "path" is
+NULL, in a per-user or system-wide (when running as root) certificate/key
+store. The generated certificate is signed by the named root certificate or,
+if "root_name" is NULL, a site-wide default root certificate. When
+"root_name" is NULL and there is no site-wide default root certificate, a
+self-signed certificate is generated instead.
+
+The "ca_cert" argument specifies whether a CA certificate should be created.
+
+The "purpose" argument specifies the purpose(s) used for the credentials as a
+bitwise OR of the following constants:
+
+
+CUPS_CREDPURPOSE_SERVER_AUTH for validating TLS servers,
+
+CUPS_CREDPURPOSE_CLIENT_AUTH for validating TLS clients,
+
+CUPS_CREDPURPOSE_CODE_SIGNING for validating compiled code,
+
+CUPS_CREDPURPOSE_EMAIL_PROTECTION for validating email messages,
+
+CUPS_CREDPURPOSE_TIME_STAMPING for signing timestamps to objects, and/or
+
+CUPS_CREDPURPOSE_OCSP_SIGNING for Online Certificate Status Protocol
+ message signing.
+
+The "type" argument specifies the type of credentials using one of the
+following constants:
+
+
+CUPS_CREDTYPE_DEFAULT: default type (RSA-3072 or P-384),
+
+CUPS_CREDTYPE_RSA_2048_SHA256: RSA with 2048-bit keys and SHA-256 hash,
+
+CUPS_CREDTYPE_RSA_3072_SHA256: RSA with 3072-bit keys and SHA-256 hash,
+
+CUPS_CREDTYPE_RSA_4096_SHA256: RSA with 4096-bit keys and SHA-256 hash,
+
+CUPS_CREDTYPE_ECDSA_P256_SHA256: ECDSA using the P-256 curve with SHA-256 hash,
+
+CUPS_CREDTYPE_ECDSA_P384_SHA256: ECDSA using the P-384 curve with SHA-256 hash, or
+
+CUPS_CREDTYPE_ECDSA_P521_SHA256: ECDSA using the P-521 curve with SHA-256 hash.
+
+The "usage" argument specifies the usage(s) for the credentials as a bitwise
+OR of the following constants:
+
+
+CUPS_CREDUSAGE_DIGITAL_SIGNATURE: digital signatures,
+
+CUPS_CREDUSAGE_NON_REPUDIATION: non-repudiation/content commitment,
+
+CUPS_CREDUSAGE_KEY_ENCIPHERMENT: key encipherment,
+
+CUPS_CREDUSAGE_DATA_ENCIPHERMENT: data encipherment,
+
+CUPS_CREDUSAGE_KEY_AGREEMENT: key agreement,
+
+CUPS_CREDUSAGE_KEY_CERT_SIGN: key certicate signing,
+
+CUPS_CREDUSAGE_CRL_SIGN: certificate revocation list signing,
+
+CUPS_CREDUSAGE_ENCIPHER_ONLY: encipherment only,
+
+CUPS_CREDUSAGE_DECIPHER_ONLY: decipherment only,
+
+CUPS_CREDUSAGE_DEFAULT_CA: defaults for CA certificates,
+
+CUPS_CREDUSAGE_DEFAULT_TLS: defaults for TLS certificates, and/or
+
+CUPS_CREDUSAGE_ALL: all usages.
+
+The "organization", "org_unit", "locality", "state_province", and "country"
+arguments specify information about the identity and geolocation of the
+issuer.
+
+The "common_name" argument specifies the common name and the "num_alt_names"
+and "alt_names" arguments specify a list of DNS hostnames for the
+certificate.
+
+The "expiration_date" argument specifies the expiration date and time as a
+Unix time_t value in seconds.
+
+Make an X.509 Certificate Signing Request.
+
+bool cupsCreateCredentialsRequest(const char *path, cups_credpurpose_t purpose, cups_credtype_t type, cups_credusage_t usage, const char *organization, const char *org_unit, const char *locality, const char *state_province, const char *country, const char *common_name, size_t num_alt_names, const char *const *alt_names);
+Parameters
+
+| path |
+Directory path for certificate/key store or NULL for default |
+| purpose |
+Credential purposes |
+| type |
+Credential type |
+| usage |
+Credential usages |
+| organization |
+Organization or NULL to use common name |
+| org_unit |
+Organizational unit or NULL for none |
+| locality |
+City/town or NULL for "Unknown" |
+| state_province |
+State/province or NULL for "Unknown" |
+| country |
+Country or NULL for locale-based default |
+| common_name |
+Common name |
+| num_alt_names |
+Number of subject alternate names |
+| alt_names |
+Subject Alternate Names |
+
+Return Value
+true on success, false on error
+Discussion
+This function creates an X.509 certificate signing request (CSR) and
+associated private key. The CSR and key are stored in the directory "path"
+or, if "path" is NULL, in a per-user or system-wide (when running as root)
+certificate/key store.
+
+The "purpose" argument specifies the purpose(s) used for the credentials as a
+bitwise OR of the following constants:
+
+
+CUPS_CREDPURPOSE_SERVER_AUTH for validating TLS servers,
+
+CUPS_CREDPURPOSE_CLIENT_AUTH for validating TLS clients,
+
+CUPS_CREDPURPOSE_CODE_SIGNING for validating compiled code,
+
+CUPS_CREDPURPOSE_EMAIL_PROTECTION for validating email messages,
+
+CUPS_CREDPURPOSE_TIME_STAMPING for signing timestamps to objects, and/or
+
+CUPS_CREDPURPOSE_OCSP_SIGNING for Online Certificate Status Protocol
+ message signing.
+
+The "type" argument specifies the type of credentials using one of the
+following constants:
+
+
+CUPS_CREDTYPE_DEFAULT: default type (RSA-3072 or P-384),
+
+CUPS_CREDTYPE_RSA_2048_SHA256: RSA with 2048-bit keys and SHA-256 hash,
+
+CUPS_CREDTYPE_RSA_3072_SHA256: RSA with 3072-bit keys and SHA-256 hash,
+
+CUPS_CREDTYPE_RSA_4096_SHA256: RSA with 4096-bit keys and SHA-256 hash,
+
+CUPS_CREDTYPE_ECDSA_P256_SHA256: ECDSA using the P-256 curve with SHA-256 hash,
+
+CUPS_CREDTYPE_ECDSA_P384_SHA256: ECDSA using the P-384 curve with SHA-256 hash, or
+
+CUPS_CREDTYPE_ECDSA_P521_SHA256: ECDSA using the P-521 curve with SHA-256 hash.
+
+The "usage" argument specifies the usage(s) for the credentials as a bitwise
+OR of the following constants:
+
+
+CUPS_CREDUSAGE_DIGITAL_SIGNATURE: digital signatures,
+
+CUPS_CREDUSAGE_NON_REPUDIATION: non-repudiation/content commitment,
+
+CUPS_CREDUSAGE_KEY_ENCIPHERMENT: key encipherment,
+
+CUPS_CREDUSAGE_DATA_ENCIPHERMENT: data encipherment,
+
+CUPS_CREDUSAGE_KEY_AGREEMENT: key agreement,
+
+CUPS_CREDUSAGE_KEY_CERT_SIGN: key certicate signing,
+
+CUPS_CREDUSAGE_CRL_SIGN: certificate revocation list signing,
+
+CUPS_CREDUSAGE_ENCIPHER_ONLY: encipherment only,
+
+CUPS_CREDUSAGE_DECIPHER_ONLY: decipherment only,
+
+CUPS_CREDUSAGE_DEFAULT_CA: defaults for CA certificates,
+
+CUPS_CREDUSAGE_DEFAULT_TLS: defaults for TLS certificates, and/or
+
+CUPS_CREDUSAGE_ALL: all usages.
+
+The "organization", "org_unit", "locality", "state_province", and "country"
+arguments specify information about the identity and geolocation of the
+issuer.
+
+The "common_name" argument specifies the common name and the "num_alt_names"
+and "alt_names" arguments specify a list of DNS hostnames for the
+certificate.
Create a job on a destination.
@@ -3757,9 +4075,11 @@
Discussion
Encode options as URL-encoded form data.
-char *cupsFormEncode(size_t num_vars, cups_option_t *vars);
+char *cupsFormEncode(const char *url, size_t num_vars, cups_option_t *vars);
Parameters
+| url |
+URL or NULL for none |
| num_vars |
Number of variables |
| vars |
@@ -3768,8 +4088,8 @@ Parameters
Return Value
URL-encoded form data
Discussion
-This function encodes a CUPS options array as URL-encoded form data,
-returning an allocated string.
+
This function encodes a CUPS options array as URL-encoded form data with an
+optional URL prefix, returning an allocated string.
Use free to return the memory used for the string.
@@ -4282,6 +4602,39 @@
const char *cupsGetUserAgent(void);
Return Value
User-Agent string
+
+Perform a HMAC function on the given data.
+
+ssize_t cupsHMACData(const char *algorithm, const unsigned char *key, size_t keylen, const void *data, size_t datalen, unsigned char *hmac, size_t hmacsize);
+Parameters
+
+| algorithm |
+Hash algorithm |
+| key |
+Key |
+| keylen |
+Length of key |
+| data |
+Data to hash |
+| datalen |
+Length of data to hash |
+| hmac |
+HMAC buffer |
+| hmacsize |
+Size of HMAC buffer |
+
+Return Value
+The length of the HMAC or -1 on error
+Discussion
+This function performs a HMAC function on the given data with the given key.
+The "algorithm" argument can be any of the registered, non-deprecated IPP
+hash algorithms for the "job-password-encryption" attribute, including
+"sha" for SHA-1, "sha2-256" for SHA2-256, etc.
+
+The "hmac" argument points to a buffer of "hmacsize" bytes and should be at
+least 64 bytes in length for all of the supported algorithms.
+
+The returned HMAC is binary data.
Perform a hash function on the given data.
@@ -4302,9 +4655,10 @@
Parameters
Return Value
Size of hash or -1 on error
Discussion
-The "algorithm" argument can be any of the registered, non-deprecated IPP
-hash algorithms for the "job-password-encryption" attribute, including
-"sha" for SHA-1, "sha-256" for SHA2-256, etc.
+
This function performs a hash function on the given data. The "algorithm"
+argument can be any of the registered, non-deprecated IPP hash algorithms for
+the "job-password-encryption" attribute, including "sha" for SHA-1,
+"sha2-256" for SHA2-256, etc.
The "hash" argument points to a buffer of "hashsize" bytes and should be at
least 64 bytes in length for all of the supported algorithms.
@@ -4338,6 +4692,33 @@
Parameters
|---|
| json |
JSON node |
+
+Save a JSON node tree to a file.
+
+bool cupsJSONExportFile(cups_json_t *json, const char *filename);
+Parameters
+
+| json |
+JSON root node |
+| filename |
+JSON filename |
+
+Return Value
+true on success, false on failure
+
+Save a JSON node tree to a string.
+
+char *cupsJSONExportString(cups_json_t *json);
+Parameters
+
+Return Value
+JSON string or NULL on error
+Discussion
+This function saves a JSON node tree to an allocated string. The resulting
+string must be freed using the free function.
Find the value(s) associated with a given key.
@@ -4450,10 +4831,10 @@
Parameters
Return Value
JSON node type
-
+
Load a JSON object file.
-cups_json_t *cupsJSONLoadFile(const char *filename);
+cups_json_t *cupsJSONImportFile(const char *filename);
Parameters
| filename |
@@ -4461,10 +4842,10 @@ Parameters
|---|
Return Value
Root JSON object node
-
+
Load a JSON object from a string.
-cups_json_t *cupsJSONLoadString(const char *s);
+cups_json_t *cupsJSONImportString(const char *s);
Parameters
| s |
@@ -4472,6 +4853,32 @@ Parameters
|---|
Return Value
Root JSON object node
+
+Load a JSON object from a URL.
+
+cups_json_t *cupsJSONImportURL(const char *url, time_t *last_modified);
+Parameters
+
+| url |
+URL |
+| last_modified |
+Last modified date/time or NULL |
+
+Return Value
+Root JSON object node
+Discussion
+This function loads a JSON object from a URL. The "url" can be a "http:" or
+"https:" URL. The "last_modified" argument provides a pointer to a time_t
+variable with the last modified date and time from a previous load. If
+NULL or the variable has a value of 0, the JSON is loaded unconditionally
+from the URL.
+
+On success, a pointer to the root JSON object node is returned and the
+"last_modified" variable, if not NULL, is updated to the Last-Modified
+date and time returned by the server. Otherwise, NULL is returned with
+the cupsLastError value set to IPP_STATUS_OK_EVENTS_COMPLETE if
+the JSON data has not been updated since the "last_modified" date and time
+or a suitable IPP_STATUS_ERROR_ value if an error occurred.
Create a new JSON node.
@@ -4532,33 +4939,219 @@
Parameters
Return Value
JSON node
-
-Save a JSON node tree to a file.
+
+Free the memory used for a JSON Web Token.
-bool cupsJSONSaveFile(cups_json_t *json, const char *filename);
+void cupsJWTDelete(cups_jwt_t *jwt);
Parameters
-| json |
-JSON root node |
-| filename |
-JSON filename |
+| jwt |
+JWT object |
+
+
+Export a JWT with the JWS Compact Serialization format.
+
+char *cupsJWTExportString(cups_jwt_t *jwt);
+Parameters
+
Return Value
-true on success, false on failure
-
-Save a JSON node tree to a string.
+JWT/JWS Compact Serialization string
+
+Get the signature algorithm used by a JSON Web Token.
-char *cupsJSONSaveString(cups_json_t *json);
+cups_jwa_t cupsJWTGetAlgorithm(cups_jwt_t *jwt);
Parameters
-| json |
-JSON root node |
+| jwt |
+JWT object |
Return Value
-JSON string or NULL on error
+Signature algorithm
+
+Get the number value of a claim.
+
+double cupsJWTGetClaimNumber(cups_jwt_t *jwt, const char *claim);
+Parameters
+
+| jwt |
+JWT object |
+| claim |
+Claim name |
+
+Return Value
+Number value
+
+Get the string value of a claim.
+
+const char *cupsJWTGetClaimString(cups_jwt_t *jwt, const char *claim);
+Parameters
+
+| jwt |
+JWT object |
+| claim |
+Claim name |
+
+Return Value
+String value
+
+Get the value type of a claim.
+
+cups_jtype_t cupsJWTGetClaimType(cups_jwt_t *jwt, const char *claim);
+Parameters
+
+| jwt |
+JWT object |
+| claim |
+Claim name |
+
+Return Value
+JSON value type
+
+Get the value node of a claim.
+
+cups_json_t *cupsJWTGetClaimValue(cups_jwt_t *jwt, const char *claim);
+Parameters
+
+| jwt |
+JWT object |
+| claim |
+Claim name |
+
+Return Value
+JSON value node
+
+Get the JWT claims as a JSON object.
+
+cups_json_t *cupsJWTGetClaims(cups_jwt_t *jwt);
+Parameters
+
+Return Value
+JSON object
+
+Determine whether the JWT has a valid signature.
+
+bool cupsJWTHasValidSignature(cups_jwt_t *jwt, cups_json_t *jwk);
+Parameters
+
+| jwt |
+JWT object |
+| jwk |
+JWK key set |
+
+Return Value
+true if value, false otherwise
+
+Import a JSON Web Token or JSON Web Signature.
+
+cups_jwt_t *cupsJWTImportString(const char *token);
+Parameters
+
+| token |
+JWS Compact Serialization string |
+
+Return Value
+JWT object
+
+Make a JSON Web Key for encryption and signing.
+
+cups_json_t *cupsJWTMakePrivateKey(cups_jwa_t alg);
+Parameters
+
+| alg |
+Signing/encryption algorithm |
+
+Return Value
+Private JSON Web Key or NULL on error
Discussion
-This function saves a JSON node tree to an allocated string. The resulting
-string must be freed using the free function.
+This function makes a JSON Web Key (JWK) for the specified JWS/JWE algorithm
+for use when signing or encrypting JSON Web Tokens. The resulting JWK
+must not be provided to clients - instead, call cupsJWTMakePublicKey
+to produce a public key subset suitable for verification and decryption.
+
+Make a JSON Web Key for decryption and verification.
+
+cups_json_t *cupsJWTMakePublicKey(cups_json_t *jwk);
+Parameters
+
+| jwk |
+Private JSON Web Key |
+
+Return Value
+Public JSON Web Key or NULL on error
+Discussion
+This function makes a public JSON Web Key (JWK) from the specified private
+JWK suitable for use when decrypting or verifying a JWE/JWS message.
+
+Create a new, empty JSON Web Token.
+
+cups_jwt_t *cupsJWTNew(const char *type);
+Parameters
+
+| type |
+JWT type or NULL for default ("JWT") |
+
+Return Value
+JWT object
+
+Set a claim number.
+
+void cupsJWTSetClaimNumber(cups_jwt_t *jwt, const char *claim, double value);
+Parameters
+
+| jwt |
+JWT object |
+| claim |
+Claim name |
+| value |
+Number value |
+
+
+Set a claim string.
+
+void cupsJWTSetClaimString(cups_jwt_t *jwt, const char *claim, const char *value);
+Parameters
+
+| jwt |
+JWT object |
+| claim |
+Claim name |
+| value |
+String value |
+
+
+Set a claim value.
+
+void cupsJWTSetClaimValue(cups_jwt_t *jwt, const char *claim, cups_json_t *value);
+Parameters
+
+| jwt |
+JWT object |
+| claim |
+Claim name |
+| value |
+JSON value node |
+
+
+Sign a JSON Web Token, creating a JSON Web Signature.
+
+bool cupsJWTSign(cups_jwt_t *jwt, cups_jwa_t alg, cups_json_t *jwk);
+Parameters
+
+| jwt |
+JWT object |
+| alg |
+Signing algorithm |
+| jwk |
+JWK key set |
+
+Return Value
+true on success, false on error
Add strings for the specified language.
@@ -5413,6 +6006,24 @@
Discussion
Note: The current credentials callback is tracked separately for each thread
in a program. Multi-threaded programs that override the callback need to do
so in each thread for the same callback to be used.
+
+Set the default server credentials.
+
+bool cupsSetServerCredentials(const char *path, const char *common_name, bool auto_create);
+Parameters
+
+| path |
+Directory path for certificate/key store or NULL for default |
+| common_name |
+Default common name for server |
+| auto_create |
+true = automatically create self-signed certificates |
+
+Return Value
+true on success, false on failure
+Discussion
+Note: The server credentials are used by all threads in the running process.
+This function is threadsafe.
Set the default user name.
@@ -5440,6 +6051,101 @@
Parameters
Discussion
Setting the string to NULL forces the default value containing the CUPS
version, IPP version, and operating system version and architecture.
+
+Sign an X.509 certificate signing request to produce an X.509 certificate chain.
+
+bool cupsSignCredentialsRequest(const char *path, const char *common_name, const char *request, const char *root_name, cups_credpurpose_t allowed_purpose, cups_credusage_t allowed_usage, cups_cert_san_cb_t cb, void *cb_data, time_t expiration_date);
+Parameters
+
+| path |
+Directory path for certificate/key store or NULL for default |
+| common_name |
+Common name to use |
+| request |
+PEM-encoded CSR |
+| root_name |
+Root certificate |
+| allowed_purpose |
+Allowed credential purpose(s) |
+| allowed_usage |
+Allowed credential usage(s) |
+| cb |
+subjectAltName callback or NULL to allow just .local |
+| cb_data |
+Callback data |
+| expiration_date |
+Certificate expiration date |
+
+Return Value
+true on success, false on failure
+Discussion
+This function creates an X.509 certificate from a signing request. The
+certificate is stored in the directory "path" or, if "path" is NULL, in a
+per-user or system-wide (when running as root) certificate/key store. The
+generated certificate is signed by the named root certificate or, if
+"root_name" is NULL, a site-wide default root certificate. When
+"root_name" is NULL and there is no site-wide default root certificate, a
+self-signed certificate is generated instead.
+
+The "allowed_purpose" argument specifies the allowed purpose(s) used for the
+credentials as a bitwise OR of the following constants:
+
+
+CUPS_CREDPURPOSE_SERVER_AUTH for validating TLS servers,
+
+CUPS_CREDPURPOSE_CLIENT_AUTH for validating TLS clients,
+
+CUPS_CREDPURPOSE_CODE_SIGNING for validating compiled code,
+
+CUPS_CREDPURPOSE_EMAIL_PROTECTION for validating email messages,
+
+CUPS_CREDPURPOSE_TIME_STAMPING for signing timestamps to objects, and/or
+
+CUPS_CREDPURPOSE_OCSP_SIGNING for Online Certificate Status Protocol
+ message signing.
+
+The "allowed_usage" argument specifies the allowed usage(s) for the
+credentials as a bitwise OR of the following constants:
+
+
+CUPS_CREDUSAGE_DIGITAL_SIGNATURE: digital signatures,
+
+CUPS_CREDUSAGE_NON_REPUDIATION: non-repudiation/content commitment,
+
+CUPS_CREDUSAGE_KEY_ENCIPHERMENT: key encipherment,
+
+CUPS_CREDUSAGE_DATA_ENCIPHERMENT: data encipherment,
+
+CUPS_CREDUSAGE_KEY_AGREEMENT: key agreement,
+
+CUPS_CREDUSAGE_KEY_CERT_SIGN: key certicate signing,
+
+CUPS_CREDUSAGE_CRL_SIGN: certificate revocation list signing,
+
+CUPS_CREDUSAGE_ENCIPHER_ONLY: encipherment only,
+
+CUPS_CREDUSAGE_DECIPHER_ONLY: decipherment only,
+
+CUPS_CREDUSAGE_DEFAULT_CA: defaults for CA certificates,
+
+CUPS_CREDUSAGE_DEFAULT_TLS: defaults for TLS certificates, and/or
+
+CUPS_CREDUSAGE_ALL: all usages.
+
+The "cb" and "cb_data" arguments specify a function and its data that are
+used to validate any subjectAltName values in the signing request:
+
+
+bool san_cb(const char *common_name, const char *alt_name, void *cb_data) {
+ ... return true if OK and false if not ...
+}
+
+
+If NULL, a default validation function is used that allows "localhost" and
+variations of the common name.
+
+The "expiration_date" argument specifies the expiration date and time as a
+Unix time_t value in seconds.
Start a new document.
@@ -6023,6 +6729,20 @@
Parameters
Return Value
New HTTP connection
+
+Copy the credentials associated with the peer in
+ an encrypted connection.
+
+bool httpCopyCredentials(http_t *http, cups_array_t **credentials);
+Parameters
+
+| http |
+Connection to server |
+| credentials |
+Array of credentials |
+
+Return Value
+Status of call (true = success)
Create a new array of HTTP credentials.
@@ -6039,6 +6759,59 @@
Return Value
Discussion
This function creates a new array of HTTP credentials for use with the
httpAddCredentials and httpSetCredentials functions.
+
+Return whether the credentials are valid
+ for the given name.
+
+bool httpCredentialsAreValidForName(cups_array_t *credentials, const char *common_name);
+Parameters
+
+| credentials |
+Credentials |
+| common_name |
+Name to check |
+
+Return Value
+true if valid, false otherwise
+
+Return the expiration date of the credentials.
+
+time_t httpCredentialsGetExpiration(cups_array_t *credentials);
+Parameters
+
+| credentials |
+Credentials |
+
+Return Value
+Expiration date of credentials
+
+Return the trust of credentials.
+
+http_trust_t httpCredentialsGetTrust(cups_array_t *credentials, const char *common_name);
+Parameters
+
+| credentials |
+Credentials |
+| common_name |
+Common name for trust lookup |
+
+Return Value
+Level of trust
+
+Return a string representing the credentials.
+
+size_t httpCredentialsString(cups_array_t *credentials, char *buffer, size_t bufsize);
+Parameters
+
+| credentials |
+Credentials |
+| buffer |
+Buffer |
+| bufsize |
+Size of buffer |
+
+Return Value
+Total size of credentials string
Base64-decode a string.
@@ -6493,6 +7266,21 @@
Return Value
true if encrypted, false if not
Discussion
This function returns true if the connection is currently encrypted.
+
+Load X.509 credentials from a keychain file.
+
+bool httpLoadCredentials(const char *path, cups_array_t **credentials, const char *common_name);
+Parameters
+
+| path |
+Keychain/PKCS#12 path |
+| credentials |
+Credentials |
+| common_name |
+Common name for credentials |
+
+Return Value
+true on success, false on error
Peek at data from a HTTP connection.
@@ -6613,6 +7401,21 @@
Discussion
bool value that is true to continue and false to stop. If no callback
is specified ("cb" is NULL), then this function will block up to 90 seconds
to resolve the specified URI.
+
+Save X.509 credentials to a keychain file.
+
+bool httpSaveCredentials(const char *path, cups_array_t *credentials, const char *common_name);
+Parameters
+
+| path |
+Keychain/PKCS#12 path |
+| credentials |
+Credentials |
+| common_name |
+Common name for credentials |
+
+Return Value
+true on success, false on error
Separate a Universal Resource Identifier into its
components.
@@ -9304,6 +10107,11 @@
typedef enum cups_bool_e cups_bool_t;
+
+Certificate signing subjectAltName callback
+
+typedef bool(*)(const char *common_name, const char *subject_alt_name, void *user_data)cups_cert_san_cb_t;
+
Client credentials callback
@@ -9314,6 +10122,21 @@
typedef pthread_cond_t cups_cond_t;
+
+Combined X.509 credential purposes for cupsCreateCredentials and cupsCreateCredentialsRequest
+
+typedef unsigned cups_credpurpose_t;
+
+
+X.509 credential types for cupsCreateCredentials and cupsCreateCredentialsRequest
+
+typedef enum cups_credtype_e cups_credtype_t;
+
+
+Combined X.509 keyUsage flags for cupsCreateCredentials and cupsCreateCredentialsRequest
+
+typedef unsigned cups_credusage_t;
+
cupsColorSpace attribute values
@@ -9332,7 +10155,12 @@
Destination enumeration callback
-typedef bool(*)(void *user_data, unsigned flags, cups_dest_t *dest)cups_dest_cb_t;
+typedef bool(*)(void *user_data, cups_dest_flags_t flags, cups_dest_t *dest)cups_dest_cb_t;
+
+
+Combined flags for cupsConnectDest and cupsEnumDests
+
+typedef unsigned cups_dest_flags_t;
Destination
@@ -9439,11 +10267,26 @@
typedef enum cups_jtype_e cups_jtype_t;
+
+JSON Web Algorithms
+
+typedef enum cups_jwa_e cups_jwa_t;
+
+
+JSON Web Token
+
+typedef struct _cups_jwt_s cups_jwt_t;
+
Language Cache
typedef struct _cups_lang_s cups_lang_t;
+
+Combined flags for cupsGetDestMediaByName and cupsGetDestMediaBySize
+
+typedef unsigned cups_media_flags_t;
+
MediaPosition values
@@ -9485,7 +10328,7 @@
typedef const char *(*)(const char *prompt, http_t *http, const char *method, const char *resource, void *user_data)cups_password_cb_t;
-Printer type/capability bits
+Combined printer type/capability flags
typedef unsigned cups_ptype_t;
@@ -9510,7 +10353,7 @@
typedef bool(*)(http_t *http, void *tls, cups_array_t *certs, void *user_data)cups_server_cert_cb_t;
-// Media Size
+Media information
typedef struct cups_size_s cups_size_t;
@@ -9953,7 +10796,7 @@ Members
Width of page image in pixels |
-// Media Size
+Media information
struct cups_size_s {
int width, length, bottom, left, right, top;
char media[128], color[128], source[128], type[128];
@@ -10009,6 +10852,47 @@
Constants
| CUPS_FALSE | Logical false |
| CUPS_TRUE | Logical true |
+
+X.509 credential purposes
+Constants
+
+| CUPS_CREDPURPOSE_ALL | All purposes |
+| CUPS_CREDPURPOSE_CLIENT_AUTH | clientAuth |
+| CUPS_CREDPURPOSE_CODE_SIGNING | codeSigning |
+| CUPS_CREDPURPOSE_EMAIL_PROTECTION | emailProtection |
+| CUPS_CREDPURPOSE_OCSP_SIGNING | OCSPSigning |
+| CUPS_CREDPURPOSE_SERVER_AUTH | serverAuth |
+| CUPS_CREDPURPOSE_TIME_STAMPING | timeStamping |
+
+
+X.509 credential types for cupsCreateCredentials and cupsCreateCredentialsRequest
+Constants
+
+| CUPS_CREDTYPE_DEFAULT | Default type |
+| CUPS_CREDTYPE_ECDSA_P256_SHA256 | ECDSA using the P-256 curve with SHA-256 hash |
+| CUPS_CREDTYPE_ECDSA_P384_SHA256 | ECDSA using the P-384 curve with SHA-256 hash |
+| CUPS_CREDTYPE_ECDSA_P521_SHA256 | ECDSA using the P-521 curve with SHA-256 hash |
+| CUPS_CREDTYPE_RSA_2048_SHA256 | RSA with 2048-bit keys and SHA-256 hash |
+| CUPS_CREDTYPE_RSA_3072_SHA256 | RSA with 3072-bit keys and SHA-256 hash |
+| CUPS_CREDTYPE_RSA_4096_SHA256 | RSA with 4096-bit keys and SHA-256 hash |
+
+
+X.509 keyUsage flags
+Constants
+
+| CUPS_CREDUSAGE_ALL | All keyUsage flags |
+| CUPS_CREDUSAGE_CRL_SIGN | cRLSign |
+| CUPS_CREDUSAGE_DATA_ENCIPHERMENT | dataEncipherment |
+| CUPS_CREDUSAGE_DECIPHER_ONLY | decipherOnly |
+| CUPS_CREDUSAGE_DEFAULT_CA | Defaults for CA certs |
+| CUPS_CREDUSAGE_DEFAULT_TLS | Defaults for TLS certs |
+| CUPS_CREDUSAGE_DIGITAL_SIGNATURE | digitalSignature |
+| CUPS_CREDUSAGE_ENCIPHER_ONLY | encipherOnly |
+| CUPS_CREDUSAGE_KEY_AGREEMENT | keyAgreement |
+| CUPS_CREDUSAGE_KEY_CERT_SIGN | keyCertSign |
+| CUPS_CREDUSAGE_KEY_ENCIPHERMENT | keyEncipherment |
+| CUPS_CREDUSAGE_NON_REPUDIATION | nonRepudiation/contentCommitment |
+
cupsColorSpace attribute values
Constants
@@ -10075,6 +10959,20 @@ Constants
| CUPS_CUT_PAGE | Cut the roll after this page |
| CUPS_CUT_SET | Cut the roll after this set |
+
+Flags for cupsConnectDest and cupsEnumDests
+Constants
+
+| CUPS_DEST_FLAGS_CANCELED | Operation was canceled |
+| CUPS_DEST_FLAGS_CONNECTING | A connection is being established |
+| CUPS_DEST_FLAGS_DEVICE | For cupsConnectDest: Connect to device |
+| CUPS_DEST_FLAGS_ERROR | An error occurred |
+| CUPS_DEST_FLAGS_MORE | There are more destinations |
+| CUPS_DEST_FLAGS_NONE | No flags are set |
+| CUPS_DEST_FLAGS_REMOVED | The destination has gone away |
+| CUPS_DEST_FLAGS_RESOLVING | The destination address is being resolved |
+| CUPS_DEST_FLAGS_UNCONNECTED | There is no connection |
+
DNS-SD callback flag values
Constants
@@ -10188,6 +11086,31 @@ Constants
| CUPS_JTYPE_STRING | String value |
| CUPS_JTYPE_TRUE | Boolean true value |
+
+JSON Web Algorithms
+Constants
+
+| CUPS_JWA_ES256 | ECDSA using P-256 and SHA-256 |
+| CUPS_JWA_ES384 | ECDSA using P-384 and SHA-384 |
+| CUPS_JWA_ES512 | ECDSA using P-521 and SHA-512 |
+| CUPS_JWA_HS256 | HMAC using SHA-256 |
+| CUPS_JWA_HS384 | HMAC using SHA-384 |
+| CUPS_JWA_HS512 | HMAC using SHA-512 |
+| CUPS_JWA_NONE | No algorithm |
+| CUPS_JWA_RS256 | RSASSA-PKCS1-v1_5 using SHA-256 |
+| CUPS_JWA_RS384 | RSASSA-PKCS1-v1_5 using SHA-384 |
+| CUPS_JWA_RS512 | RSASSA-PKCS1-v1_5 using SHA-512 |
+
+
+Flags for cupsGetDestMediaByName and cupsGetDestMediaBySize
+Constants
+
+| CUPS_MEDIA_FLAGS_BORDERLESS | Find a borderless size |
+| CUPS_MEDIA_FLAGS_DEFAULT | Find the closest size supported by the printer |
+| CUPS_MEDIA_FLAGS_DUPLEX | Find a size compatible with 2-sided printing |
+| CUPS_MEDIA_FLAGS_EXACT | Find an exact match for the size |
+| CUPS_MEDIA_FLAGS_READY | If the printer supports media sensing, find the size amongst the "ready" media. |
+
MediaPosition values
Constants
@@ -10261,7 +11184,7 @@ Constants
| CUPS_ORIENT_90 | Rotate the page counter-clockwise |
-Printer type/capability bit constants
+Printer type/capability flags
Constants
| CUPS_PRINTER_AUTHENTICATED | Printer requires authentication |
diff --git a/examples/Makefile b/examples/Makefile
index bdda03ded..0f5395dd7 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -104,6 +104,13 @@ all:
libs:
+#
+# Run unit tests...
+#
+
+test:
+
+
#
# Make unit tests...
#
diff --git a/man/Makefile b/man/Makefile
index 355c302b0..1ca31e5ad 100644
--- a/man/Makefile
+++ b/man/Makefile
@@ -1,7 +1,7 @@
#
# Man page makefile for libcups.
#
-# Copyright © 2021 by OpenPrinting.
+# Copyright © 2021-2023 by OpenPrinting.
# Copyright © 2007-2019 by Apple Inc.
# Copyright © 1993-2006 by Easy Software Products.
#
@@ -33,7 +33,7 @@ all: $(LOCALTARGET)
#
-# Make unit tests...
+# Run unit tests...
#
test:
@@ -85,10 +85,10 @@ uninstall:
#
-# Local programs (not built when cross-compiling...)
+# Unit test programs (not built when cross-compiling...)
#
-local:
+unittests:
#
diff --git a/tools/Makefile b/tools/Makefile
index af2eadc96..f1fac06a7 100644
--- a/tools/Makefile
+++ b/tools/Makefile
@@ -25,8 +25,7 @@ IPPTOOLS = \
ippfind \
ipptool
TARGETS = \
- $(IPPTOOLS) \
- $(LOCALTARGET)
+ $(IPPTOOLS)
#
@@ -100,17 +99,17 @@ uninstall:
#
-# Local programs (not built when cross-compiling...)
+# Unit test programs (not built when cross-compiling...)
#
-local: ippeveprinter-static ippfind-static ipptool-static
+unittests: ippeveprinter-static ippfind-static ipptool-static
#
# Test everything...
#
-test: local
+test: unittests
./run-tests.sh
diff --git a/tools/ippeveprinter.c b/tools/ippeveprinter.c
index a19dcc989..a384d7adb 100644
--- a/tools/ippeveprinter.c
+++ b/tools/ippeveprinter.c
@@ -1586,7 +1586,7 @@ create_printer(
* Extract up to 3 icons...
*/
- for (i = 1, iconsptr = strchr(icons, ','); iconsptr && i < 3; i ++, iconsptr = strchr(iconsptr, ','))
+ for (i = 1, iconsptr = strchr(printer->icons[0], ','); iconsptr && i < 3; i ++, iconsptr = strchr(iconsptr, ','))
{
*iconsptr++ = '\0';
printer->icons[i] = iconsptr;
@@ -5995,7 +5995,10 @@ process_job(ippeve_job_t *job) /* I - Job */
}
if (mystdout < 0)
- mystdout = open("/dev/null", O_WRONLY | O_BINARY);
+ {
+ if ((mystdout = open("/dev/null", O_WRONLY | O_BINARY)) < 0)
+ fprintf(stderr, "[Job %d] Unable to redirect command output to /dev/null: %s", job->id, strerror(errno));
+ }
if (pipe(mypipe))
{
diff --git a/tools/ipptool.c b/tools/ipptool.c
index 51d4acd13..d45a89bd6 100644
--- a/tools/ipptool.c
+++ b/tools/ipptool.c
@@ -4878,6 +4878,7 @@ print_json_attr(
default :
/* Out-of-band value */
+ cupsFilePrintf(data->outfile, ": null");
break;
}
}
diff --git a/xcode/libcups.xcodeproj/project.pbxproj b/xcode/libcups.xcodeproj/project.pbxproj
index 19a04fc46..6d7fa02ed 100644
--- a/xcode/libcups.xcodeproj/project.pbxproj
+++ b/xcode/libcups.xcodeproj/project.pbxproj
@@ -30,6 +30,7 @@
2797D73728E5D24E0056D546 /* PBXTargetDependency */,
2794DA8D29DCBDEB00B71A3F /* PBXTargetDependency */,
271284B91CC11FA500E517C7 /* PBXTargetDependency */,
+ 27C2A7A72A0C59EA00C20AEF /* PBXTargetDependency */,
271284BF1CC11FA500E517C7 /* PBXTargetDependency */,
27D3967927BA8224003D3D8E /* PBXTargetDependency */,
274770E42345347D0089BC31 /* PBXTargetDependency */,
@@ -135,6 +136,9 @@
2712865D1CC1309000E517C7 /* libcups_static.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 72A4332F155844CF002E172D /* libcups_static.a */; };
2712865E1CC1309000E517C7 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2767FC591926750C000F61D3 /* CoreFoundation.framework */; };
271286691CC130C700E517C7 /* tlscheck.c in Sources */ = {isa = PBXBuildFile; fileRef = 271286681CC130BD00E517C7 /* tlscheck.c */; };
+ 271607952A0EC955002508F6 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 27D3967A27BB389C003D3D8E /* libz.tbd */; };
+ 271607962A0EC96B002508F6 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 27D3968227BB390D003D3D8E /* libresolv.tbd */; };
+ 271607972A0EC971002508F6 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 27D3967E27BB38E6003D3D8E /* libiconv.tbd */; };
273B1EA1226B3E4800428143 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2767FC591926750C000F61D3 /* CoreFoundation.framework */; };
273B1EA2226B3E4800428143 /* libresolv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2767FC5B1926750C000F61D3 /* libresolv.dylib */; };
273B1EA3226B3E4800428143 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 2767FC5C1926750C000F61D3 /* libz.dylib */; };
@@ -770,6 +774,13 @@
remoteGlobalIDString = 2797D72228E5D2360056D546;
remoteInfo = testjson;
};
+ 27C2A7A62A0C59EA00C20AEF /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 72BF96371333042100B1EAD7 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 724FA5F71CC038190092477B;
+ remoteInfo = testpwg;
+ };
27D3966A27BA81D3003D3D8E /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 72BF96371333042100B1EAD7 /* Project object */;
@@ -1716,7 +1727,10 @@
buildActionMask = 2147483647;
files = (
279B960D28C582F000869E97 /* SystemConfiguration.framework in Frameworks */,
+ 271607952A0EC955002508F6 /* libz.tbd in Frameworks */,
+ 271607972A0EC971002508F6 /* libiconv.tbd in Frameworks */,
27CAFC7C282C8C6F00FE02FC /* libcrypto.a in Frameworks */,
+ 271607962A0EC96B002508F6 /* libresolv.tbd in Frameworks */,
27CAFC7D282C8C6F00FE02FC /* libssl.a in Frameworks */,
724FA5FF1CC038190092477B /* libcups_static.a in Frameworks */,
724FA6001CC038190092477B /* CoreFoundation.framework in Frameworks */,
@@ -2665,7 +2679,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
- LastUpgradeCheck = 1420;
+ LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "Apple Inc.";
TargetAttributes = {
270695FD1CADF3E200FFE5FB = {
@@ -3284,6 +3298,11 @@
target = 2797D72228E5D2360056D546 /* testjson */;
targetProxy = 2797D73628E5D24E0056D546 /* PBXContainerItemProxy */;
};
+ 27C2A7A72A0C59EA00C20AEF /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 724FA5F71CC038190092477B /* testpwg */;
+ targetProxy = 27C2A7A62A0C59EA00C20AEF /* PBXContainerItemProxy */;
+ };
27D3966927BA81D3003D3D8E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 274FF6891333B1C400317ECB /* libcups3_static */;
diff --git a/xcode/libcups.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/xcode/libcups.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme
index 6d2f41f0b..60892d3d4 100644
--- a/xcode/libcups.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme
+++ b/xcode/libcups.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme
@@ -1,6 +1,6 @@