From fd7dee836d0b14efc48bf59955c8a12a72561043 Mon Sep 17 00:00:00 2001
From: Stef Walter <stefw@collabora.co.uk>
Date: Fri, 24 Jun 2011 15:31:02 +0200
Subject: Add P11KitPin structure, which encapsulates a returned pin.

 * Lets us use variable size buffers.
 * Helps minimize copying.
---
 p11-kit/pin.c            | 171 +++++++++++++++++++++++++++++++++++++++++++----
 p11-kit/pin.h            |  44 ++++++++----
 tests/files/test-pinfile |   1 +
 tests/pin-test.c         | 161 ++++++++++++++++++++++++++++++--------------
 4 files changed, 302 insertions(+), 75 deletions(-)
 create mode 100644 tests/files/test-pinfile

diff --git a/p11-kit/pin.c b/p11-kit/pin.c
index 14cb171..879b6b7 100644
--- a/p11-kit/pin.c
+++ b/p11-kit/pin.c
@@ -42,11 +42,14 @@
 #include "pin.h"
 #include "private.h"
 #include "ptr-array.h"
+#include "util.h"
 
 #include <assert.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 /**
  * SECTION:p11-pin
@@ -138,7 +141,7 @@ typedef struct _PinfileCallback {
 	/* Readonly after construct */
 	p11_kit_pin_callback func;
 	void *user_data;
-	p11_kit_pin_callback_destroy destroy;
+	p11_kit_pin_destroy_func destroy;
 } PinfileCallback;
 
 /*
@@ -173,7 +176,7 @@ unref_pinfile_callback (void *pointer)
 
 int
 p11_kit_pin_register_callback (const char *pinfile, p11_kit_pin_callback callback,
-                               void *callback_data, p11_kit_pin_callback_destroy callback_destroy)
+                               void *callback_data, p11_kit_pin_destroy_func callback_destroy)
 {
 	PinfileCallback *cb;
 	ptr_array_t *callbacks;
@@ -283,16 +286,15 @@ p11_kit_pin_unregister_callback (const char *pinfile, p11_kit_pin_callback callb
 	_p11_unlock ();
 }
 
-int
+P11KitPin*
 p11_kit_pin_retrieve (const char *pinfile, P11KitUri *pin_uri,
-                      const char *pin_description, P11KitPinFlags flags,
-                      char *pin, size_t pin_length)
+                      const char *pin_description, P11KitPinFlags flags)
 {
 	PinfileCallback **snapshot = NULL;
 	unsigned int snapshot_count = 0;
 	ptr_array_t *callbacks;
+	P11KitPin *pin;
 	unsigned int i;
-	int ret;
 
 	_p11_lock ();
 
@@ -315,12 +317,11 @@ p11_kit_pin_retrieve (const char *pinfile, P11KitUri *pin_uri,
 	_p11_unlock ();
 
 	if (snapshot == NULL)
-		return 0;
+		return NULL;
 
-	ret = 0;
-	for (i = snapshot_count; ret == 0 && i > 0; i--) {
-		ret = (snapshot[i - 1]->func) (pinfile, pin_uri, pin_description, flags,
-		                               snapshot[i - 1]->user_data, pin, pin_length);
+	for (pin = NULL, i = snapshot_count; pin == NULL && i > 0; i--) {
+		pin = (snapshot[i - 1]->func) (pinfile, pin_uri, pin_description, flags,
+		                               snapshot[i - 1]->user_data);
 	}
 
 	_p11_lock ();
@@ -329,5 +330,151 @@ p11_kit_pin_retrieve (const char *pinfile, P11KitUri *pin_uri,
 		free (snapshot);
 	_p11_unlock ();
 
-	return ret;
+	return pin;
+}
+
+P11KitPin*
+p11_kit_pin_file_callback (const char *pinfile,
+                           P11KitUri *pin_uri,
+                           const char *pin_description,
+                           P11KitPinFlags pin_flags,
+                           void *callback_data)
+{
+	unsigned char *buffer;
+	size_t used, allocated;
+	int error = 0;
+	int fd;
+	int res;
+
+	/* We don't support retries */
+	if (pin_flags & P11_KIT_PIN_FLAGS_RETRY)
+		return NULL;
+
+	fd = open (pinfile, O_RDONLY);
+	if (fd == -1)
+		return NULL;
+
+	buffer = NULL;
+	used = 0;
+	allocated = 0;
+
+	for (;;) {
+		if (used + 256 > allocated) {
+			buffer = xrealloc (buffer, used + 1024);
+			if (buffer == NULL) {
+				error = ENOMEM;
+				break;
+			}
+			allocated = used + 1024;
+		}
+
+		res = read (fd, buffer + used, allocated - used);
+		if (res < 0) {
+			if (errno == EAGAIN)
+				continue;
+			error = errno;
+			free (buffer);
+			buffer = NULL;
+			error = errno;
+			break;
+		} else if (res == 0) {
+			break;
+		} else {
+			used += res;
+		}
+	}
+
+	if (buffer == NULL) {
+		errno = error;
+		return NULL;
+	}
+
+	return p11_kit_pin_new_for_buffer (buffer, used, free);
+}
+
+struct _P11KitPin {
+	int ref_count;
+	unsigned char *buffer;
+	size_t length;
+	p11_kit_pin_destroy_func destroy;
+};
+
+P11KitPin*
+p11_kit_pin_new (const unsigned char *value, size_t length)
+{
+	unsigned char *copy;
+	P11KitPin *pin;
+
+	copy = malloc (length);
+	if (copy == NULL)
+		return NULL;
+
+	memcpy (copy, value, length);
+	pin = p11_kit_pin_new_for_buffer (copy, length, free);
+	if (pin == NULL)
+		free (copy);
+	return pin;
+}
+
+P11KitPin*
+p11_kit_pin_new_for_string (const char *value)
+{
+	return p11_kit_pin_new ((const unsigned char *)value, strlen (value));
+}
+
+P11KitPin*
+p11_kit_pin_new_for_buffer (unsigned char *buffer, size_t length,
+                            p11_kit_pin_destroy_func destroy)
+{
+	P11KitPin *pin;
+
+	pin = calloc (1, sizeof (P11KitPin));
+	if (pin == NULL)
+		return NULL;
+
+	pin->ref_count = 1;
+	pin->buffer = buffer;
+	pin->length = length;
+	pin->destroy = destroy;
+
+	return pin;
+}
+
+const unsigned char*
+p11_kit_pin_get_value (P11KitPin *pin, size_t *length)
+{
+	if (length)
+		*length = pin->length;
+	return pin->buffer;
+}
+
+P11KitPin*
+p11_kit_pin_ref (P11KitPin *pin)
+{
+	_p11_lock ();
+
+		pin->ref_count++;
+
+	_p11_unlock ();
+
+	return pin;
+}
+
+void
+p11_kit_pin_unref (P11KitPin *pin)
+{
+	int last = 0;
+
+	_p11_lock ();
+
+		last = (pin->ref_count == 1);
+		pin->ref_count--;
+
+	_p11_unlock ();
+
+	if (last) {
+		if (pin->destroy)
+			(pin->destroy) (pin->buffer);
+		free (pin);
+	}
 }
diff --git a/p11-kit/pin.h b/p11-kit/pin.h
index bb5daae..532aa54 100644
--- a/p11-kit/pin.h
+++ b/p11-kit/pin.h
@@ -41,6 +41,8 @@
 extern "C" {
 #endif
 
+typedef struct _P11KitPin P11KitPin;
+
 typedef enum {
 	P11_KIT_PIN_FLAGS_USER_LOGIN = 1,
 	P11_KIT_PIN_FLAGS_SO_LOGIN = 2,
@@ -52,31 +54,49 @@ typedef enum {
 
 #define P11_KIT_PIN_FALLBACK ""
 
-typedef int         (*p11_kit_pin_callback)                 (const char *pinfile,
+typedef void        (*p11_kit_pin_destroy_func)             (void *callback_data);
+
+P11KitPin*            p11_kit_pin_new                       (const unsigned char *value,
+                                                             size_t length);
+
+P11KitPin*            p11_kit_pin_new_for_string            (const char *value);
+
+P11KitPin*            p11_kit_pin_new_for_buffer            (unsigned char *buffer,
+                                                             size_t length,
+                                                             p11_kit_pin_destroy_func destroy);
+
+P11KitPin*            p11_kit_pin_ref                       (P11KitPin *pin);
+
+void                  p11_kit_pin_unref                     (P11KitPin *pin);
+
+const unsigned char * p11_kit_pin_get_value                 (P11KitPin *pin,
+                                                             size_t *length);
+
+typedef P11KitPin*  (*p11_kit_pin_callback)                 (const char *pinfile,
                                                              P11KitUri *pin_uri,
                                                              const char *pin_description,
                                                              P11KitPinFlags pin_flags,
-                                                             void *callback_data,
-                                                             char *pin,
-                                                             size_t pin_length);
-
-typedef void        (*p11_kit_pin_callback_destroy)         (void *callback_data);
+                                                             void *callback_data);
 
-int                 p11_kit_pin_register_callback           (const char *pinfile,
+int                   p11_kit_pin_register_callback         (const char *pinfile,
                                                              p11_kit_pin_callback callback,
                                                              void *callback_data,
-                                                             p11_kit_pin_callback_destroy callback_destroy);
+                                                             p11_kit_pin_destroy_func callback_destroy);
 
-void                p11_kit_pin_unregister_callback         (const char *pinfile,
+void                  p11_kit_pin_unregister_callback       (const char *pinfile,
                                                              p11_kit_pin_callback callback,
                                                              void *callback_data);
 
-int                 p11_kit_pin_retrieve                    (const char *pinfile,
+P11KitPin*            p11_kit_pin_retrieve                  (const char *pinfile,
+                                                             P11KitUri *pin_uri,
+                                                             const char *pin_description,
+                                                             P11KitPinFlags pin_flags);
+
+P11KitPin*            p11_kit_pin_file_callback             (const char *pinfile,
                                                              P11KitUri *pin_uri,
                                                              const char *pin_description,
                                                              P11KitPinFlags pin_flags,
-                                                             char *pin,
-                                                             size_t pin_max);
+                                                             void *callback_data);
 
 #ifdef __cplusplus
 } /* extern "C" */
diff --git a/tests/files/test-pinfile b/tests/files/test-pinfile
new file mode 100644
index 0000000..f646f3d
--- /dev/null
+++ b/tests/files/test-pinfile
@@ -0,0 +1 @@
+yogabbagabba
\ No newline at end of file
diff --git a/tests/pin-test.c b/tests/pin-test.c
index 344fe6b..086f892 100644
--- a/tests/pin-test.c
+++ b/tests/pin-test.c
@@ -36,31 +36,28 @@
 #include "CuTest.h"
 
 #include <assert.h>
+#include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 
 #include "p11-kit/pin.h"
 
-static int
+static P11KitPin *
 callback_one (const char *pinfile, P11KitUri *pin_uri, const char *pin_description,
-              P11KitPinFlags pin_flags, void *callback_data, char *pin,
-              size_t pin_max)
+              P11KitPinFlags pin_flags, void *callback_data)
 {
 	int *data = callback_data;
 	assert (*data == 33);
-	strncpy (pin, "one", pin_max);
-	return 1;
+	return p11_kit_pin_new_for_buffer ((unsigned char*)strdup ("one"), 3, free);
 }
 
-static int
+static P11KitPin*
 callback_other (const char *pinfile, P11KitUri *pin_uri, const char *pin_description,
-                P11KitPinFlags pin_flags, void *callback_data, char *pin,
-                size_t pin_max)
+                P11KitPinFlags pin_flags, void *callback_data)
 {
 	char *data = callback_data;
-	strncpy (pin, data, pin_max);
-	return 1;
+	return p11_kit_pin_new_for_string (data);
 }
 
 static void
@@ -88,50 +85,54 @@ static void
 test_pin_read (CuTest *tc)
 {
 	P11KitUri *uri;
-	char buffer[256];
+	P11KitPin *pin;
 	int data = 33;
-	int ret;
+	size_t length;
+	const unsigned char *ptr;
 
 	p11_kit_pin_register_callback ("/the/pinfile", callback_one,
 	                               &data, destroy_data);
 
 	uri = p11_kit_uri_new ();
-	ret = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
-	                            P11_KIT_PIN_FLAGS_USER_LOGIN,
-	                            buffer, sizeof (buffer));
+	pin = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
+	                            P11_KIT_PIN_FLAGS_USER_LOGIN);
 	p11_kit_uri_free (uri);
 
-	CuAssertIntEquals (tc, 1, ret);
-	CuAssertStrEquals (tc, "one", buffer);
+	CuAssertPtrNotNull (tc, pin);
+	ptr = p11_kit_pin_get_value (pin, &length);
+	CuAssertIntEquals (tc, 3, length);
+	CuAssertTrue (tc, memcmp (ptr, "one", 3) == 0);
 
 	p11_kit_pin_unregister_callback ("/the/pinfile", callback_one,
 	                                 &data);
+
+	p11_kit_pin_ref (pin);
+	p11_kit_pin_unref (pin);
 }
 
 static void
 test_pin_read_no_match (CuTest *tc)
 {
 	P11KitUri *uri;
-	char buffer[256];
-	int ret;
+	P11KitPin *pin;
 
 	uri = p11_kit_uri_new ();
-	ret = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
-	                            P11_KIT_PIN_FLAGS_USER_LOGIN,
-	                            buffer, sizeof (buffer));
+	pin = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
+	                            P11_KIT_PIN_FLAGS_USER_LOGIN);
 	p11_kit_uri_free (uri);
 
-	CuAssertIntEquals (tc, 0, ret);
+	CuAssertPtrEquals (tc, NULL, pin);
 }
 
 static void
 test_pin_register_duplicate (CuTest *tc)
 {
 	P11KitUri *uri;
+	P11KitPin *pin;
 	char *value = "secret";
-	char buffer[256];
 	int data = 33;
-	int ret;
+	size_t length;
+	const unsigned char *ptr;
 
 	uri = p11_kit_uri_new ();
 
@@ -141,31 +142,34 @@ test_pin_register_duplicate (CuTest *tc)
 	p11_kit_pin_register_callback ("/the/pinfile", callback_other,
 	                               value, NULL);
 
-	ret = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
-	                            P11_KIT_PIN_FLAGS_USER_LOGIN,
-	                            buffer, sizeof (buffer));
+	pin = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
+	                            P11_KIT_PIN_FLAGS_USER_LOGIN);
 
-	CuAssertIntEquals (tc, 1, ret);
-	CuAssertStrEquals (tc, "secret", buffer);
+	CuAssertPtrNotNull (tc, pin);
+	ptr = p11_kit_pin_get_value (pin, &length);
+	CuAssertIntEquals (tc, 6, length);
+	CuAssertTrue (tc, memcmp (ptr, "secret", length) == 0);
+	p11_kit_pin_unref (pin);
 
 	p11_kit_pin_unregister_callback ("/the/pinfile", callback_other,
 	                                 value);
 
-	ret = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
-	                            P11_KIT_PIN_FLAGS_USER_LOGIN,
-	                            buffer, sizeof (buffer));
+	pin = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
+	                            P11_KIT_PIN_FLAGS_USER_LOGIN);
 
-	CuAssertIntEquals (tc, 1, ret);
-	CuAssertStrEquals (tc, "one", buffer);
+	CuAssertPtrNotNull (tc, pin);
+	ptr = p11_kit_pin_get_value (pin, &length);
+	CuAssertIntEquals (tc, 3, length);
+	CuAssertTrue (tc, memcmp (ptr, "one", length) == 0);
+	p11_kit_pin_unref (pin);
 
 	p11_kit_pin_unregister_callback ("/the/pinfile", callback_one,
 	                                 &data);
 
-	ret = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
-	                                P11_KIT_PIN_FLAGS_USER_LOGIN,
-	                                buffer, sizeof (buffer));
+	pin = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
+	                            P11_KIT_PIN_FLAGS_USER_LOGIN);
 
-	CuAssertIntEquals (tc, 0, ret);
+	CuAssertPtrEquals (tc, NULL, pin);
 
 	p11_kit_uri_free (uri);
 }
@@ -175,31 +179,36 @@ test_pin_register_fallback (CuTest *tc)
 {
 	char *value = "secret";
 	P11KitUri *uri;
-	char buffer[256];
+	P11KitPin *pin;
 	int data = 33;
-	int ret;
+	size_t length;
+	const unsigned char *ptr;
 
 	uri = p11_kit_uri_new ();
 
 	p11_kit_pin_register_callback (P11_KIT_PIN_FALLBACK, callback_one,
 	                               &data, destroy_data);
 
-	ret = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
-	                            P11_KIT_PIN_FLAGS_USER_LOGIN,
-	                            buffer, sizeof (buffer));
+	pin = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
+	                            P11_KIT_PIN_FLAGS_USER_LOGIN);
 
-	CuAssertIntEquals (tc, 1, ret);
-	CuAssertStrEquals (tc, "one", buffer);
+	CuAssertPtrNotNull (tc, pin);
+	ptr = p11_kit_pin_get_value (pin, &length);
+	CuAssertIntEquals (tc, 3, length);
+	CuAssertTrue (tc, memcmp (ptr, "one", length) == 0);
+	p11_kit_pin_unref (pin);
 
 	p11_kit_pin_register_callback ("/the/pinfile", callback_other,
 	                               value, NULL);
 
-	ret = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
-	                            P11_KIT_PIN_FLAGS_USER_LOGIN,
-	                            buffer, sizeof (buffer));
+	pin = p11_kit_pin_retrieve ("/the/pinfile", uri, "The token",
+	                            P11_KIT_PIN_FLAGS_USER_LOGIN);
 
-	CuAssertIntEquals (tc, 1, ret);
-	CuAssertStrEquals (tc, "secret", buffer);
+	CuAssertPtrNotNull (tc, pin);
+	ptr = p11_kit_pin_get_value (pin, &length);
+	CuAssertIntEquals (tc, 6, length);
+	CuAssertTrue (tc, memcmp (ptr, "secret", length) == 0);
+	p11_kit_pin_unref (pin);
 
 	p11_kit_pin_unregister_callback ("/the/pinfile", callback_other,
 	                                 value);
@@ -210,6 +219,54 @@ test_pin_register_fallback (CuTest *tc)
 	p11_kit_uri_free (uri);
 }
 
+static void
+test_pin_file (CuTest *tc)
+{
+	P11KitUri *uri;
+	P11KitPin *pin;
+	size_t length;
+	const unsigned char *ptr;
+
+	uri = p11_kit_uri_new ();
+
+	p11_kit_pin_register_callback (P11_KIT_PIN_FALLBACK, p11_kit_pin_file_callback,
+	                               NULL, NULL);
+
+	pin = p11_kit_pin_retrieve (SRCDIR "/files/test-pinfile", uri, "The token",
+	                            P11_KIT_PIN_FLAGS_USER_LOGIN);
+
+	CuAssertPtrNotNull (tc, pin);
+	ptr = p11_kit_pin_get_value (pin, &length);
+	CuAssertIntEquals (tc, 12, length);
+	CuAssertTrue (tc, memcmp (ptr, "yogabbagabba", length) == 0);
+	p11_kit_pin_unref (pin);
+
+	pin = p11_kit_pin_retrieve (SRCDIR "/files/nonexistant", uri, "The token",
+	                            P11_KIT_PIN_FLAGS_USER_LOGIN);
+
+	CuAssertPtrEquals (tc, NULL, pin);
+
+	p11_kit_pin_unregister_callback (P11_KIT_PIN_FALLBACK, p11_kit_pin_file_callback,
+	                                 NULL);
+
+	p11_kit_uri_free (uri);
+}
+
+static void
+test_pin_ref_unref (CuTest *tc)
+{
+	P11KitPin *pin;
+	P11KitPin *check;
+
+	pin = p11_kit_pin_new_for_string ("crack of lies");
+
+	check = p11_kit_pin_ref (pin);
+	CuAssertPtrEquals (tc, pin, check);
+
+	p11_kit_pin_unref (pin);
+	p11_kit_pin_unref (check);
+}
+
 int
 main (void)
 {
@@ -222,6 +279,8 @@ main (void)
 	SUITE_ADD_TEST (suite, test_pin_read_no_match);
 	SUITE_ADD_TEST (suite, test_pin_register_duplicate);
 	SUITE_ADD_TEST (suite, test_pin_register_fallback);
+	SUITE_ADD_TEST (suite, test_pin_file);
+	SUITE_ADD_TEST (suite, test_pin_ref_unref);
 
 	CuSuiteRun (suite);
 	CuSuiteSummary (suite, output);
-- 
cgit v1.1