diff --git a/src/openvpn/buffer.c b/src/openvpn/buffer.c
index 046e231..5ce2e39 100644
--- a/src/openvpn/buffer.c
+++ b/src/openvpn/buffer.c
@@ -1384,3 +1384,27 @@
     fclose(fp);
     return ret;
 }
+
+char *
+buf_extract_field(struct buffer *buf, char sep, struct gc_arena *gc)
+{
+    if (!buf_valid(buf))
+    {
+        return NULL;
+    }
+
+    const uint8_t *seppos = memchr(BPTR(buf), sep, buf_len(buf));
+    if (!seppos)
+    {
+        return NULL;
+    }
+    size_t field_len = seppos - BPTR(buf);
+
+    char *field = gc_malloc(field_len + 1, false, gc);
+
+    memcpy(field, BPTR(buf), field_len);
+    field[field_len] = 0;
+
+    buf_advance(buf, field_len + 1);
+    return field;
+}
diff --git a/src/openvpn/buffer.h b/src/openvpn/buffer.h
index bc5cffb..4471697 100644
--- a/src/openvpn/buffer.h
+++ b/src/openvpn/buffer.h
@@ -2243,4 +2243,14 @@
 /**@}*/
 /* End of Buffer Lists */
 
+/**
+ * Extract a field from buf that ends with the \c sep character. The
+ * returned string is allocated in the gc_arena. If the separator character
+ * is not found, the function returns the nullptr.
+ *
+ * The buffer is also forwarded to the point after the separator character.
+ */
+char *
+buf_extract_field(struct buffer *buf, char sep, struct gc_arena *gc);
+
 #endif /* BUFFER_H */
diff --git a/tests/unit_tests/openvpn/test_buffer.c b/tests/unit_tests/openvpn/test_buffer.c
index ce38bbe..7fb9162 100644
--- a/tests/unit_tests/openvpn/test_buffer.c
+++ b/tests/unit_tests/openvpn/test_buffer.c
@@ -456,6 +456,35 @@
     /* Check that our own method agrees */
     assert_true(string_check_buf(&buf2, CC_PRINT | CC_NULL, CC_CRLF));
     assert_string_equal(BSTR(&buf2), "CR_RESPONSE,MTIx");
+    gc_free(&gc);
+}
+
+static void
+test_buffer_extract_field(void **state)
+{
+    struct gc_arena gc = gc_new();
+    struct buffer buf = alloc_buf_gc(1000, &gc);
+    assert_null(buf_extract_field(&buf, ',', &gc));
+
+    buf = alloc_buf_gc(5, &gc);
+    buf_write(&buf, "12345", 5);
+    const char *ret = buf_extract_field(&buf, '5', &gc);
+    assert_string_equal(ret, "1234");
+    /* nothing left after the 5 */
+    assert_int_equal(buf_len(&buf), 0);
+
+    buf = alloc_buf_gc(5, &gc);
+    buf_write(&buf, "12345", 5);
+    ret = buf_extract_field(&buf, '4', &gc);
+    assert_string_equal(ret, "123");
+
+    /* 5 should be left */
+    assert_int_equal(buf_len(&buf), 1);
+    assert_memory_equal(buf_bptr(&buf), "5", 1);
+
+    buf = alloc_buf_gc(5, &gc);
+    buf_write(&buf, "12345", 5);
+    assert_null(buf_extract_field(&buf, '6', &gc));
 
     gc_free(&gc);
 }
@@ -567,7 +596,8 @@
         cmocka_unit_test(test_checked_snprintf),
         cmocka_unit_test(test_buffer_chomp),
         cmocka_unit_test(test_buffer_null_terminate),
-        cmocka_unit_test(test_buffer_parse)
+        cmocka_unit_test(test_buffer_parse),
+        cmocka_unit_test(test_buffer_extract_field)
     };
 
     return cmocka_run_group_tests_name("buffer", tests, NULL, NULL);
