aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRuss Allbery <rra@stanford.edu>2008-04-07 19:43:02 +0000
committerRuss Allbery <rra@stanford.edu>2008-04-07 19:43:02 +0000
commit307ba7704617f8473644e571a6d63aba8de53dec (patch)
treef370092c6b91bb325ec3eb67471f1149b46051f5
parent5c7f925d454d16cf6427f3d3e9e2a5f0299ca085 (diff)
Add support for enabling and disabling principals (clearing or setting
the NOTGS flag) and examining principals to kasetkey. This functionality isn't used by wallet (and probably won't be) but is convenient for other users of kasetkey such as kadmin-remctl.
-rw-r--r--LICENSE6
-rw-r--r--Makefile.am11
-rw-r--r--NEWS5
-rw-r--r--configure.ac2
-rw-r--r--kasetkey/kasetkey.c221
-rw-r--r--portable/strlcat.c42
-rw-r--r--portable/strlcpy.c40
-rw-r--r--system.h6
-rw-r--r--tests/TESTS2
-rw-r--r--tests/portable/strlcat-t.c77
-rw-r--r--tests/portable/strlcat.c2
-rw-r--r--tests/portable/strlcpy-t.c72
-rw-r--r--tests/portable/strlcpy.c2
13 files changed, 426 insertions, 62 deletions
diff --git a/LICENSE b/LICENSE
index 24be219..a04578d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -50,10 +50,12 @@ The file portable/snprintf.c is released under the following license:
It may be used for any purpose as long as this notice remains intact
on all source code distributions
-The files portable/asprintf.c, portable/dummy.c and util/concat.c have
-been placed in the public domain by their author.
+The files portable/asprintf.c, portable/dummy.c, portable/strlcat.c,
+portable/strlcpy.c, and util/concat.c have been placed in the public
+domain by their author.
The files tests/libtest.c, tests/libtest.h, tests/portable/snprintf-t.c,
+tests/portable/strlcat-t.c, tests/portable/strlcpy-t.c,
tests/util/concat-t.c, tests/util/messages-t.c, tests/util/xmalloc-t, and
tests/util/xmalloc.c are released under the following copyright and
license:
diff --git a/Makefile.am b/Makefile.am
index ba1ff21..fe0a812 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -121,7 +121,8 @@ clean-local:
# The bits below are for the test suite, not for the main package.
check_PROGRAMS = tests/runtests tests/portable/asprintf-t \
- tests/portable/snprintf-t tests/util/concat-t tests/util/messages-t \
+ tests/portable/snprintf-t tests/portable/strlcat-t \
+ tests/portable/strlcpy-t tests/util/concat-t tests/util/messages-t \
tests/util/xmalloc
check_LIBRARIES = tests/libtest.a
tests_libtest_a_SOURCES = tests/libtest.c tests/libtest.h
@@ -135,6 +136,14 @@ tests_portable_snprintf_t_SOURCES = tests/portable/snprintf-t.c \
tests/portable/snprintf.c
tests_portable_snprintf_t_LDADD = tests/libtest.a util/libutil.a \
portable/libportable.a
+tests_portable_strlcat_t_SOURCES = tests/portable/strlcat-t.c \
+ tests/portable/strlcat.c
+tests_portable_strlcat_t_LDADD = tests/libtest.a util/libutil.a \
+ portable/libportable.a
+tests_portable_strlcpy_t_SOURCES = tests/portable/strlcpy-t.c \
+ tests/portable/strlcpy.c
+tests_portable_strlcpy_t_LDADD = tests/libtest.a util/libutil.a \
+ portable/libportable.a
tests_util_concat_t_LDADD = tests/libtest.a util/libutil.a \
portable/libportable.a
tests_util_messages_t_LDADD = tests/libtest.a util/libutil.a \
diff --git a/NEWS b/NEWS
index 8be1343..d56a472 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,11 @@
wallet 0.9 (unreleased)
+ Add support for enabling and disabling principals (clearing or setting
+ the NOTGS flag) and examining principals to kasetkey. This
+ functionality isn't used by wallet (and probably won't be) but is
+ convenient for other users of kasetkey such as kadmin-remctl.
+
Report the correct error message when addprinc fails while creating a
keytab object.
diff --git a/configure.ac b/configure.ac
index 7530691..4f722fc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -26,7 +26,7 @@ RRA_C_GNU_VAMACROS
AC_CHECK_TYPES([long long])
RRA_FUNC_SNPRINTF
AC_CHECK_FUNCS([setrlimit])
-AC_REPLACE_FUNCS([asprintf])
+AC_REPLACE_FUNCS([asprintf strlcat strlcpy])
AC_ARG_WITH([wallet-server],
[AC_HELP_STRING([--with-wallet-server=HOST], [Default wallet server])],
diff --git a/kasetkey/kasetkey.c b/kasetkey/kasetkey.c
index 19c1ffd..8bcc950 100644
--- a/kasetkey/kasetkey.c
+++ b/kasetkey/kasetkey.c
@@ -1,19 +1,19 @@
-/* $Id$
-**
-** Create or change a principal and/or generate a srvtab.
-**
-** Written by Roland Schemers <schemers@stanford.edu>
-** Updated by Russ Allbery <rra@stanford.edu>
-** Updated again by AAU, Anton Ushakov <antonu@stanford.edu>
-** Copyright 1994, 1998, 1999, 2000, 2006, 2007
-** Board of Trustees, Leland Stanford Jr. University
-**
-** See LICENSE for licensing terms.
-**
-** Sets the key of a principal in the AFS kaserver given a srvtab. This
-** program is now used for synchronization of K5 and K4 and nothing else.
-** It will no longer be used once K4 is retired.
-*/
+/* $Id$
+ *
+ * Create or change a principal and/or generate a srvtab.
+ *
+ * Sets the key of a principal in the AFS kaserver given a srvtab, enables or
+ * disables a principal, or displays information about a principal in an AFS
+ * kaserver.
+ *
+ * Written by Roland Schemers <schemers@stanford.edu>
+ * Updated by Russ Allbery <rra@stanford.edu>
+ * Updated again by Anton Ushakov <antonu@stanford.edu>
+ * Copyright 1994, 1998, 1999, 2000, 2006, 2007, 2008
+ * Board of Trustees, Leland Stanford Jr. University
+ *
+ * See LICENSE for licensing terms.
+ */
#include <config.h>
@@ -37,6 +37,8 @@
#include <afs/cellconfig.h>
#include <ubik.h>
+#include <util/util.h>
+
/* Normally set by the AFS libraries. */
#ifndef SNAME_SZ
# define SNAME_SZ 40
@@ -44,9 +46,11 @@
# define REALM_SZ 40
#endif
-/* AFS currently doesn't prototype this function. Cheat on the first argument
- since it actually takes a function with a completely variable argument
- list. */
+/*
+ * AFS currently doesn't prototype this function. Cheat on the first argument
+ * since it actually takes a function with a completely variable argument
+ * list.
+ */
#if !HAVE_DECL_UBIK_CALL
afs_int32 ubik_Call(void *, struct ubik_client *, afs_int32, ...);
#endif
@@ -60,12 +64,15 @@ struct config {
int debug; /* Whether to enable debugging. */
int init; /* Keyfile initialization. */
int random; /* Randomize the key. */
+ int tgs; /* Enable the principal. */
+ int notgs; /* Disable the princial. */
char *keyfile; /* Name of srvtab to use. */
char *admin; /* Name of ADMIN user to use. */
char *password; /* Password to use. */
char *srvtab; /* srvtab file to generate. */
- char *service; /* Service principal to create. */
- char *delete; /* Service principal to delete. */
+ char *service; /* Principal to create/enable. */
+ char *delete; /* Principal to delete. */
+ char *examine; /* Principal to examine. */
char *k5srvtab; /* K5 converted srvtab to read for key. */
};
@@ -75,14 +82,17 @@ Usage: %s [options]\n\
-a adminuser Admin user\n\
-c k5srvtab Use the key from the given srvtab (for sync w/ K5)\n\
-D service Name of service to delete\n\
- -d turn on debugging\n\
+ -d Turn on debugging\n\
+ -e principal Examine the given principal\n\
-f srvtab Name of srvtab file to create\n\
-h This help\n\
-i Initialize DES key file\n\
-k keyfile File containing srvtab for admin user\n\
+ -n Set the principal NOTGS\n\
-p password Use given password to create key\n\
-r Use random key\n\
-s service Name of service to create\n\
+ -t Set the principal TGS\n\
-v Print version\n\
\n\
To create a srvtab for rcmd.slapshot and be prompted for the admin\n\
@@ -101,40 +111,6 @@ and then create a srvtab for rcmd.slapshot with:\n\
\n";
-/* Report a fatal error. */
-static void
-die(const char *format, ...)
-{
- va_list args;
-
- if (program != NULL)
- fprintf(stderr, "%s: ", program);
- va_start(args, format);
- vfprintf(stderr, format, args);
- va_end(args);
- fprintf(stderr, "\n");
- exit(1);
-}
-
-
-/* Report a fatal error, including strerror information. */
-static void
-sysdie(const char *format, ...)
-{
- int oerrno;
- va_list args;
-
- oerrno = errno;
- if (program != NULL)
- fprintf(stderr, "%s: ", program);
- va_start(args, format);
- vfprintf(stderr, format, args);
- va_end(args);
- fprintf(stderr, ": %s\n", strerror(oerrno));
- exit(1);
-}
-
-
/*
* Print out the usage message and then exit with the status given as the only
* argument. If status is zero, the message is printed to standard output;
@@ -282,7 +258,9 @@ authenticate(struct config *config, struct ktc_token *token)
}
-/* Delete a principal out of the AFS kaserver. */
+/*
+ * Delete a principal out of the AFS kaserver.
+ */
static void
delete_principal(struct config *config)
{
@@ -314,6 +292,122 @@ delete_principal(struct config *config)
/*
+ * Format a date. The output format expects ctime-style date formatting, so
+ * we use that. Takes a buffer into which to put the date. There will be a
+ * trailing newline.
+ */
+static void
+format_date(char *buffer, size_t size, time_t date)
+{
+ if (date == (time_t) NEVERDATE)
+ strlcpy(buffer, "never\n", size);
+ else
+ strlcpy(buffer, ctime(&date), size);
+}
+
+
+/*
+ * Enable or disable a principal in the AFS kaserver (by setting or clearing
+ * the NOTGS flag). The second argument says to enable if it's true, disable
+ * otherwise.
+ */
+static void
+enable_principal(struct config *config, int enable)
+{
+ struct ktc_token token;
+ struct ubik_client *conn;
+ struct kaentryinfo entry;
+ char name[MAXKTCNAMELEN];
+ char inst[MAXKTCNAMELEN];
+ char cell[MAXKTCNAMELEN];
+ long code;
+
+ /* Make connection to AuthServer. */
+ authenticate(config, &token);
+ parse_principal(config, config->delete, name, inst, cell);
+ code = ka_AuthServerConn(cell, KA_MAINTENANCE_SERVICE, &token, &conn);
+ if (config->debug)
+ printf("ka_AuthServerConn %s %ld\n", cell, code);
+ if (code != 0)
+ die("can't make connection to auth server");
+
+ /* Retrieve the principal information. */
+ code = ubik_Call(KAM_GetEntry, conn, 0, name, inst, KAMAJORVERSION,
+ &entry);
+ if (config->debug)
+ printf("ubik_Call KAM_GetEntry %ld\n", code);
+ if (code != 0) {
+ if (code == KANOENT)
+ die("no such entry in the database");
+ else
+ die("can't retrieve principal information");
+ }
+
+ /* Set the flags. */
+ if (enable)
+ entry.flags &= ~KAFNOTGS;
+ else
+ entry.flags |= KAFNOTGS;
+ code = ubik_Call(KAM_SetFields, conn, 0, name, inst, entry.flags, 0, 0,
+ -1, 0, 0);
+ if (config->debug)
+ printf("ubik_Call KAM_SetFields %ld\n", code);
+ if (code != 0)
+ die("can't %s principal", enable ? "enable" : "disable");
+ code = ubik_ClientDestroy(conn);
+ exit(0);
+}
+
+
+/*
+ * Examine a principal. The output format is compatible with the old Stanford
+ * Kerberos v4 kadmin, which may be compatible with Kerberos v4 kadmin in
+ * general (I haven't checked).
+ */
+static void
+examine_principal(struct config *config)
+{
+ struct ktc_token token;
+ struct ubik_client *conn;
+ struct kaentryinfo entry;
+ char name[MAXKTCNAMELEN];
+ char inst[MAXKTCNAMELEN];
+ char cell[MAXKTCNAMELEN];
+ long code;
+ char edate[64], cdate[64], mdate[64];
+
+ /* Make connection to AuthServer. */
+ authenticate(config, &token);
+ parse_principal(config, config->delete, name, inst, cell);
+ code = ka_AuthServerConn(cell, KA_MAINTENANCE_SERVICE, &token, &conn);
+ if (config->debug)
+ printf("ka_AuthServerConn %s %ld\n", cell, code);
+ if (code != 0)
+ die("can't make connection to auth server");
+
+ /* Retrieve and format the entry. */
+ code = ubik_Call(KAM_GetEntry, conn, 0, name, inst, KAMAJORVERSION,
+ &entry);
+ if (config->debug)
+ printf("ubik_Call KAM_GetEntry %ld\n", code);
+ if (code != 0)
+ die("can't retrieve current flags");
+ format_date(edate, sizeof(edate), entry.user_expiration);
+ format_date(mdate, sizeof(cdate), entry.modification_time);
+ format_date(cdate, sizeof(mdate), entry.change_password_time);
+ printf("status: %s\n", (entry.flags & KAFNOTGS) ? "disabled" : "enabled");
+ printf("account expiration: %s", edate);
+ printf("password last changed: %s", cdate);
+ printf("modification time: %s", mdate);
+ printf("modified by: %s%s%s\n", entry.modification_user.name,
+ (entry.modification_user.instance[0] != '\0') ? "." : "",
+ entry.modification_user.instance);
+ code = ubik_ClientDestroy(conn);
+ exit(0);
+}
+
+
+/*
* Create a new principal in the AFS kaserver (deleting it and recreating it
* if it already exists) with either the indicated key or with a random key,
* and then write out a srvtab for that principal. Also supported is reading
@@ -447,12 +541,15 @@ main(int argc, char *argv[])
case 'c': config.k5srvtab = optarg; break;
case 'D': config.delete = optarg; break;
case 'd': config.debug = 1; break;
+ case 'e': config.examine = optarg; break;
case 'f': config.srvtab = optarg; break;
case 'i': config.init = 1; break;
case 'k': config.keyfile = optarg; break;
+ case 'n': config.notgs = 1; break;
case 'p': config.password = optarg; break;
case 'r': config.random = 1; break;
case 's': config.service = optarg; break;
+ case 't': config.tgs = 1; break;
/* Usage doesn't return. */
case 'h':
@@ -468,10 +565,18 @@ main(int argc, char *argv[])
/* Take the right action. */
if (config.random && config.k5srvtab)
usage(1);
+ if (config.notgs && config.tgs)
+ die("cannot set principal both TGS and NOTGS at the same time");
+ if ((config.notgs || config.tgs) && config.service == NULL)
+ die("must specify a principal with -s");
if (config.debug)
- fprintf(stdout,"cell: %s\n", config.local_cell);
+ fprintf(stdout, "cell: %s\n", config.local_cell);
if (config.init)
initialize_admin_srvtab(&config);
+ else if (config.tgs || config.notgs)
+ enable_principal(&config, config.tgs);
+ else if (config.examine != NULL)
+ examine_principal(&config);
else if (config.service != NULL)
generate_srvtab(&config);
else if (config.delete != NULL)
diff --git a/portable/strlcat.c b/portable/strlcat.c
new file mode 100644
index 0000000..971a348
--- /dev/null
+++ b/portable/strlcat.c
@@ -0,0 +1,42 @@
+/* $Id$
+ *
+ * Replacement for a missing strlcat.
+ *
+ * Provides the same functionality as the *BSD function strlcat, originally
+ * developed by Todd Miller and Theo de Raadt. strlcat works similarly to
+ * strncat, except simpler. The result is always nul-terminated even if the
+ * source string is longer than the space remaining in the destination string,
+ * and the total space required is returned. The third argument is the total
+ * space available in the destination buffer, not just the amount of space
+ * remaining.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * This work is hereby placed in the public domain by its author.
+ */
+
+#include <config.h>
+#include <system.h>
+
+/*
+ * If we're running the test suite, rename strlcat to avoid conflicts with
+ * the system version.
+ */
+#if TESTING
+# define strlcat test_strlcat
+size_t test_strlcat(char *, const char *, size_t);
+#endif
+
+size_t
+strlcat(char *dst, const char *src, size_t size)
+{
+ size_t used, length, copy;
+
+ used = strlen(dst);
+ length = strlen(src);
+ if (size > 0 && used < size - 1) {
+ copy = (length >= size - used) ? size - used - 1 : length;
+ memcpy(dst + used, src, copy);
+ dst[used + copy] = '\0';
+ }
+ return used + length;
+}
diff --git a/portable/strlcpy.c b/portable/strlcpy.c
new file mode 100644
index 0000000..1dd49a3
--- /dev/null
+++ b/portable/strlcpy.c
@@ -0,0 +1,40 @@
+/* $Id$
+ *
+ * Replacement for a missing strlcpy.
+ *
+ * Provides the same functionality as the *BSD function strlcpy, originally
+ * developed by Todd Miller and Theo de Raadt. strlcpy works similarly to
+ * strncpy, except saner and simpler. The result is always nul-terminated
+ * even if the source string is longer than the destination string, and the
+ * total space required is returned. The destination string is not nul-filled
+ * like strncpy does, just nul-terminated.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ * This work is hereby placed in the public domain by its author.
+ */
+
+#include <config.h>
+#include <system.h>
+
+/*
+ * If we're running the test suite, rename strlcpy to avoid conflicts with
+ * the system version.
+ */
+#if TESTING
+# define strlcpy test_strlcpy
+size_t test_strlcpy(char *, const char *, size_t);
+#endif
+
+size_t
+strlcpy(char *dst, const char *src, size_t size)
+{
+ size_t length, copy;
+
+ length = strlen(src);
+ if (size > 0) {
+ copy = (length >= size) ? size - 1 : length;
+ memcpy(dst, src, copy);
+ dst[copy] = '\0';
+ }
+ return length;
+}
diff --git a/system.h b/system.h
index cbe555f..0650a2d 100644
--- a/system.h
+++ b/system.h
@@ -73,6 +73,12 @@ extern int snprintf(char *, size_t, const char *, ...)
#if !HAVE_DECL_VSNPRINTF
extern int vsnprintf(char *, size_t, const char *, va_list);
#endif
+#if !HAVE_STRLCAT
+extern size_t strlcat(char *, const char *, size_t);
+#endif
+#if !HAVE_STRLCPY
+extern size_t strlcpy(char *, const char *, size_t);
+#endif
END_DECLS
diff --git a/tests/TESTS b/tests/TESTS
index 5bae099..4ff4a3b 100644
--- a/tests/TESTS
+++ b/tests/TESTS
@@ -3,6 +3,8 @@ client/full
client/pod
portable/asprintf
portable/snprintf
+portable/strlcat
+portable/strlcpy
server/admin
server/backend
server/keytab
diff --git a/tests/portable/strlcat-t.c b/tests/portable/strlcat-t.c
new file mode 100644
index 0000000..5ad8b6f
--- /dev/null
+++ b/tests/portable/strlcat-t.c
@@ -0,0 +1,77 @@
+/* $Id$
+ *
+ * strlcat test suite.
+ *
+ * Copyright (c) 2004, 2005, 2006
+ * by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ * 2002, 2003 by The Internet Software Consortium and Rich Salz
+ *
+ * This code is derived from software contributed to the Internet Software
+ * Consortium by Rich Salz.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+#include <system.h>
+
+#include <tests/libtest.h>
+
+size_t test_strlcat(char *, const char *, size_t);
+
+
+int
+main(void)
+{
+ char buffer[10] = "";
+
+ test_init(27);
+
+ ok_int(1, 3, test_strlcat(buffer, "foo", sizeof(buffer)));
+ ok_string(2, "foo", buffer);
+ ok_int(3, 7, test_strlcat(buffer, " bar", sizeof(buffer)));
+ ok_string(4, "foo bar", buffer);
+ ok_int(5, 9, test_strlcat(buffer, "!!", sizeof(buffer)));
+ ok_string(6, "foo bar!!", buffer);
+ ok_int(7, 10, test_strlcat(buffer, "!", sizeof(buffer)));
+ ok_string(8, "foo bar!!", buffer);
+ ok(9, buffer[9] == '\0');
+ buffer[0] = '\0';
+ ok_int(10, 11, test_strlcat(buffer, "hello world", sizeof(buffer)));
+ ok_string(11, "hello wor", buffer);
+ ok(12, buffer[9] == '\0');
+ buffer[0] = '\0';
+ ok_int(13, 7, test_strlcat(buffer, "sausage", 5));
+ ok_string(14, "saus", buffer);
+ ok_int(15, 14, test_strlcat(buffer, "bacon eggs", sizeof(buffer)));
+ ok_string(16, "sausbacon", buffer);
+
+ /* Make sure that with a size of 0, the destination isn't changed. */
+ ok_int(17, 11, test_strlcat(buffer, "!!", 0));
+ ok_string(18, "sausbacon", buffer);
+
+ /* Now play with empty strings. */
+ ok_int(19, 9, test_strlcat(buffer, "", 0));
+ ok_string(20, "sausbacon", buffer);
+ buffer[0] = '\0';
+ ok_int(21, 0, test_strlcat(buffer, "", sizeof(buffer)));
+ ok_string(22, "", buffer);
+ ok_int(23, 3, test_strlcat(buffer, "foo", 2));
+ ok_string(24, "f", buffer);
+ ok(25, buffer[1] == '\0');
+ ok_int(26, 1, test_strlcat(buffer, "", sizeof(buffer)));
+ ok(27, buffer[1] == '\0');
+
+ return 0;
+}
diff --git a/tests/portable/strlcat.c b/tests/portable/strlcat.c
new file mode 100644
index 0000000..8983bd8
--- /dev/null
+++ b/tests/portable/strlcat.c
@@ -0,0 +1,2 @@
+#define TESTING 1
+#include <portable/strlcat.c>
diff --git a/tests/portable/strlcpy-t.c b/tests/portable/strlcpy-t.c
new file mode 100644
index 0000000..6427374
--- /dev/null
+++ b/tests/portable/strlcpy-t.c
@@ -0,0 +1,72 @@
+/* $Id$
+ *
+ * strlcpy test suite.
+ *
+ * Copyright (c) 2004, 2005, 2006
+ * by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+ * 2002, 2003 by The Internet Software Consortium and Rich Salz
+ *
+ * This code is derived from software contributed to the Internet Software
+ * Consortium by Rich Salz.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <config.h>
+#include <system.h>
+
+#include <tests/libtest.h>
+
+size_t test_strlcpy(char *, const char *, size_t);
+
+
+int
+main(void)
+{
+ char buffer[10];
+
+ test_init(23);
+
+ ok_int(1, 3, test_strlcpy(buffer, "foo", sizeof(buffer)));
+ ok_string(2, "foo", buffer);
+ ok_int(3, 9, test_strlcpy(buffer, "hello wor", sizeof(buffer)));
+ ok_string(4, "hello wor", buffer);
+ ok_int(5, 10, test_strlcpy(buffer, "world hell", sizeof(buffer)));
+ ok_string(6, "world hel", buffer);
+ ok(7, buffer[9] == '\0');
+ ok_int(8, 11, test_strlcpy(buffer, "hello world", sizeof(buffer)));
+ ok_string(9, "hello wor", buffer);
+ ok(10, buffer[9] == '\0');
+
+ /* Make sure that with a size of 0, the destination isn't changed. */
+ ok_int(11, 3, test_strlcpy(buffer, "foo", 0));
+ ok_string(12, "hello wor", buffer);
+
+ /* Now play with empty strings. */
+ ok_int(13, 0, test_strlcpy(buffer, "", 0));
+ ok_string(14, "hello wor", buffer);
+ ok_int(15, 0, test_strlcpy(buffer, "", sizeof(buffer)));
+ ok_string(16, "", buffer);
+ ok_int(17, 3, test_strlcpy(buffer, "foo", 2));
+ ok_string(18, "f", buffer);
+ ok(19, buffer[1] == '\0');
+ ok_int(20, 0, test_strlcpy(buffer, "", 1));
+ ok(21, buffer[0] == '\0');
+
+ /* Finally, check using strlcpy as strlen. */
+ ok_int(22, 3, test_strlcpy(NULL, "foo", 0));
+ ok_int(23, 11, test_strlcpy(NULL, "hello world", 0));
+
+ return 0;
+}
diff --git a/tests/portable/strlcpy.c b/tests/portable/strlcpy.c
new file mode 100644
index 0000000..d444595
--- /dev/null
+++ b/tests/portable/strlcpy.c
@@ -0,0 +1,2 @@
+#define TESTING 1
+#include <portable/strlcpy.c>