mirror of
git://git.gnu.org.ua/pam-modules.git
synced 2025-04-26 00:19:52 +03:00
303 lines
6.4 KiB
C
303 lines
6.4 KiB
C
/* This file is part of pam-modules.
|
||
Copyright (C) 2008-2022 Sergey Poznyakoff
|
||
|
||
This program is free software; you can redistribute it and/or modify it
|
||
under the terms of the GNU General Public License as published by the
|
||
Free Software Foundation; either version 3 of the License, or (at your
|
||
option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License along
|
||
with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||
|
||
#include <graypam.h>
|
||
|
||
#define GRAY_SLIST_BUCKET_SIZE 1024
|
||
|
||
struct gray_slist_bucket {
|
||
struct gray_slist_bucket *next;
|
||
char *buf;
|
||
size_t level;
|
||
size_t size;
|
||
};
|
||
|
||
struct gray_slist {
|
||
struct gray_slist_bucket *head, *tail;
|
||
struct gray_slist_bucket *free;
|
||
int ec; /* error code */
|
||
};
|
||
|
||
static struct gray_slist_bucket *
|
||
alloc_bucket(size_t size)
|
||
{
|
||
struct gray_slist_bucket *p = malloc(sizeof(*p) + size);
|
||
if (p) {
|
||
p->buf = (char*)(p + 1);
|
||
p->level = 0;
|
||
p->size = size;
|
||
p->next = NULL;
|
||
}
|
||
return p;
|
||
}
|
||
|
||
static int
|
||
alloc_pool(gray_slist_t slist, size_t size)
|
||
{
|
||
struct gray_slist_bucket *p = alloc_bucket(GRAY_SLIST_BUCKET_SIZE);
|
||
if (!p) {
|
||
slist->ec = errno;
|
||
return 1;
|
||
}
|
||
if (slist->tail)
|
||
slist->tail->next = p;
|
||
else
|
||
slist->head = p;
|
||
slist->tail = p;
|
||
return 0;
|
||
}
|
||
|
||
static ssize_t
|
||
copy_chars(gray_slist_t slist, const char *str, size_t n)
|
||
{
|
||
size_t rest;
|
||
|
||
if (!slist->head || slist->tail->level == slist->tail->size) {
|
||
if (alloc_pool(slist, GRAY_SLIST_BUCKET_SIZE))
|
||
return -1;
|
||
}
|
||
rest = slist->tail->size - slist->tail->level;
|
||
if (n > rest)
|
||
n = rest;
|
||
memcpy(slist->tail->buf + slist->tail->level, str, n);
|
||
slist->tail->level += n;
|
||
return n;
|
||
}
|
||
|
||
/* Create new string list. */
|
||
gray_slist_t
|
||
gray_slist_create(void)
|
||
{
|
||
gray_slist_t slist = malloc(sizeof(*slist));
|
||
if (slist) {
|
||
slist->head = slist->tail = slist->free = 0;
|
||
slist->ec = 0;
|
||
}
|
||
return slist;
|
||
}
|
||
|
||
/* Return error code associated with the string list */
|
||
int
|
||
gray_slist_err(gray_slist_t slist)
|
||
{
|
||
return slist->ec;
|
||
}
|
||
|
||
/* Clear error code associated with the string list */
|
||
void
|
||
gray_slist_clerr(gray_slist_t slist)
|
||
{
|
||
slist->ec = 0;
|
||
}
|
||
|
||
/* Clear content of the string list */
|
||
void
|
||
gray_slist_clear(gray_slist_t slist)
|
||
{
|
||
if (slist->tail) {
|
||
slist->tail->next = slist->free;
|
||
slist->free = slist->head;
|
||
slist->head = slist->tail = NULL;
|
||
}
|
||
gray_slist_clerr(slist);
|
||
}
|
||
|
||
/* Deallocate memory used by the string list. Store NULL in *SLIST */
|
||
void
|
||
gray_slist_free(gray_slist_t *slist)
|
||
{
|
||
if (*slist) {
|
||
struct gray_slist_bucket *p;
|
||
gray_slist_clear(*slist);
|
||
for (p = (*slist)->free; p; ) {
|
||
struct gray_slist_bucket *next = p->next;
|
||
free(p);
|
||
p = next;
|
||
}
|
||
}
|
||
free(*slist);
|
||
*slist = NULL;
|
||
}
|
||
|
||
/* Append N bytes from the string STR to the list. Return the number of
|
||
bytes actually stored. */
|
||
ssize_t
|
||
gray_slist_append(gray_slist_t slist, const char *str, size_t n)
|
||
{
|
||
ssize_t total;
|
||
|
||
if (slist->ec)
|
||
return -1;
|
||
total = 0;
|
||
while (total < n) {
|
||
ssize_t s = copy_chars(slist, str + total, n - total);
|
||
if (s == -1)
|
||
break;
|
||
total += s;
|
||
}
|
||
return total;
|
||
}
|
||
|
||
/* Append one character to the list. Return 1 on success. */
|
||
ssize_t
|
||
gray_slist_append_char(gray_slist_t slist, char c)
|
||
{
|
||
return gray_slist_append(slist, &c, 1);
|
||
}
|
||
|
||
/* Return the total size of strings stored in the list. */
|
||
size_t
|
||
gray_slist_size(gray_slist_t slist)
|
||
{
|
||
size_t size = 0;
|
||
struct gray_slist_bucket *p;
|
||
for (p = slist->head; p; p = p->next)
|
||
size += p->level;
|
||
return size;
|
||
}
|
||
|
||
/* Coalesce all segments in the list into a single contiguous string.
|
||
Return its length. */
|
||
ssize_t
|
||
gray_slist_coalesce(gray_slist_t slist)
|
||
{
|
||
size_t size;
|
||
|
||
if (slist->ec)
|
||
return -1;
|
||
else if (slist->head && slist->head->next == NULL)
|
||
size = slist->head->level;
|
||
else {
|
||
size = gray_slist_size(slist);
|
||
struct gray_slist_bucket *bucket, *p;
|
||
|
||
bucket = alloc_bucket(size);
|
||
if (!bucket)
|
||
return -1;
|
||
|
||
for (p = slist->head; p; ) {
|
||
struct gray_slist_bucket *next = p->next;
|
||
memcpy(bucket->buf + bucket->level, p->buf, p->level);
|
||
bucket->level += p->level;
|
||
free(p);
|
||
p = next;
|
||
}
|
||
slist->head = slist->tail = bucket;
|
||
}
|
||
return size;
|
||
}
|
||
|
||
/* Return the pointer to the head of the list. */
|
||
void *
|
||
gray_slist_head(gray_slist_t slist, size_t *psize)
|
||
{
|
||
if (*psize)
|
||
*psize = slist->head ? slist->head->level : 0;
|
||
return slist->head ? slist->head->buf : NULL;
|
||
}
|
||
|
||
/* Finish building the string list. Coalesce all segments, and clear the
|
||
unused memory. On success, return the pointer to the newly-built string.
|
||
On error (out of memory), return NULL and set error status in the SLIST. */
|
||
void *
|
||
gray_slist_finish(gray_slist_t slist)
|
||
{
|
||
if (slist->ec)
|
||
return NULL;
|
||
if (gray_slist_coalesce(slist) == -1)
|
||
return NULL;
|
||
gray_slist_clear(slist);
|
||
return slist->free->buf;
|
||
}
|
||
|
||
|
||
#define to_num(c) \
|
||
(isdigit(c) ? c - '0' : (isxdigit(c) ? toupper(c) - 'A' + 10 : 255 ))
|
||
|
||
int
|
||
gray_slist_grow_backslash_num(gray_slist_t slist, char *text, char **pend,
|
||
int len, int base)
|
||
{
|
||
int i;
|
||
int val = 0;
|
||
char *start = text;
|
||
|
||
if (slist->ec)
|
||
return -1;
|
||
if (text[0] == '\\') {
|
||
text++;
|
||
if (base == 16)
|
||
text++;
|
||
}
|
||
|
||
for (i = 0; i < len; i++) {
|
||
int n = (unsigned char)text[i];
|
||
if (n > 127 || (n = to_num(n)) >= base)
|
||
break;
|
||
val = val*base + n;
|
||
}
|
||
|
||
if (i == 0) {
|
||
if (gray_slist_append(slist, start, 1) != 1)
|
||
return -1;
|
||
if (pend)
|
||
*pend = start + 1;
|
||
} else {
|
||
if (gray_slist_append_char(slist, val) != 1)
|
||
return -1;
|
||
if (pend)
|
||
*pend = text + i;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int
|
||
gray_decode_backslash(int c)
|
||
{
|
||
static char transtab[] = "a\ab\bf\fn\nr\rt\t";
|
||
char *p;
|
||
|
||
for (p = transtab; *p; p += 2) {
|
||
if (*p == c)
|
||
return p[1];
|
||
}
|
||
return c;
|
||
}
|
||
|
||
int
|
||
gray_slist_grow_backslash(gray_slist_t slist, char *text, char **endp)
|
||
{
|
||
if (text[1] == '\\' || (unsigned char)text[1] > 127) {
|
||
if (gray_slist_append_char(slist, text[1]) != 1)
|
||
return -1;
|
||
text += 2;
|
||
} else if (isdigit(text[1])) {
|
||
if (gray_slist_grow_backslash_num(slist, text, &text, 3, 8))
|
||
return -1;
|
||
} else if (text[1] == 'x' || text[1] == 'X') {
|
||
if (gray_slist_grow_backslash_num(slist, text, &text, 2, 16))
|
||
return -1;
|
||
} else {
|
||
int c = gray_decode_backslash(text[1]);
|
||
if (gray_slist_append_char(slist, c) != 1)
|
||
return -1;
|
||
text += 2;
|
||
}
|
||
|
||
*endp = text;
|
||
return 0;
|
||
}
|
||
|