mirror of
https://github.com/Yubico/yubico-pam.git
synced 2025-02-20 12:54:16 +01:00
Merge commit 'aa87979eb84adb3adef170dac6ff2285ba43cd26' into features/ldap
Conflicts: README
This commit is contained in:
commit
f579f256c0
30
README
30
README
@ -20,7 +20,7 @@ later. This has introduced a dependency of libykpers-1 from the
|
||||
ykpersonalize package. Pass `--without-cr` to `configure` to avoid
|
||||
this dependency.
|
||||
|
||||
The development community is co-ordinated via
|
||||
The development community is co-ordinated via
|
||||
https://github.com/Yubico/yubico-pam[the GitHub project page].
|
||||
|
||||
The project is licensed under a BSD license. See the file COPYING for
|
||||
@ -150,7 +150,7 @@ service, go https://upgrade.yubico.com/getapikey[here].
|
||||
|
||||
debug:: to enable debug output to stdout.
|
||||
|
||||
alwaysok::
|
||||
alwaysok::
|
||||
to enable all authentication attempts to succeed
|
||||
(aka presentation mode).
|
||||
|
||||
@ -310,10 +310,10 @@ information including the OTP and ID of your token to the shell -- copy the ID
|
||||
into your config file and you should be up and going.
|
||||
|
||||
------
|
||||
Yubikey for `youruser':
|
||||
Yubikey for `youruser':
|
||||
[pam_yubico.c:pam_sm_authenticate(867)] conv returned 44 bytes
|
||||
[pam_yubico.c:pam_sm_authenticate(885)] Skipping first 0 bytes. Length is 44, token_id set to 12 and token OTP always 32.
|
||||
[pam_yubico.c:pam_sm_authenticate(892)] OTP: ccccccclabcabkhbdncicglfltnukadfoifadfhhhhfe ID: cccccclabcab
|
||||
[pam_yubico.c:pam_sm_authenticate(892)] OTP: ccccccclabcabkhbdncicglfltnukadfoifadfhhhhfe ID: cccccclabcab
|
||||
------
|
||||
|
||||
|
||||
@ -336,5 +336,25 @@ Examples
|
||||
If you want to use the YubiKey to authenticate you on Linux console
|
||||
logins, add the following to the top of `/etc/pam.d/login`:
|
||||
|
||||
auth sufficient pam_yubico.so id=16 debug
|
||||
auth sufficient pam_yubico.so id=16 debug
|
||||
|
||||
OpenVPN and ActiveDirectory
|
||||
---------------------------
|
||||
|
||||
create file '/etc/pam.d/openvpn':
|
||||
auth required pam_yubico.so ldap_uri=ldap://ldap-srv debug id=19 yubi_attr=pager
|
||||
ldapdn=dc=ad,dc=next-audience,dc=net
|
||||
ldap_filter=(&(sAMAccountName=%u)(memberOf=CN=mygroup,OU=DefaultUser,DC=adivser,DC=net))
|
||||
ldap_bind_no_anonymous ldap_bind_user_filter=%u@adviser.com try_first_pass
|
||||
account required pam_yubico.so
|
||||
|
||||
create file 'openvpn.conf'
|
||||
plugin openvpn-plugin-auth-pam.so openvpn
|
||||
|
||||
|
||||
Feedback
|
||||
--------
|
||||
|
||||
If you want to discuss anything related to the Yubico PAM module,
|
||||
please e-mail the mailing list yubico-devel@googlegroups.com.
|
||||
|
||||
|
175
pam_yubico.c
175
pam_yubico.c
@ -113,6 +113,12 @@ struct cfg
|
||||
const char *urllist;
|
||||
const char *ldapserver;
|
||||
const char *ldap_uri;
|
||||
int ldap_bind_no_anonymous;
|
||||
const char *ldap_bind_user_filter;
|
||||
const char *ldap_bind_user;
|
||||
const char *ldap_bind_password;
|
||||
const char *ldap_filter;
|
||||
const char *ldap_cacertfile;
|
||||
const char *ldapdn;
|
||||
const char *user_attr;
|
||||
const char *yubi_attr;
|
||||
@ -193,8 +199,10 @@ free_out:
|
||||
* This function will look in ldap id the token correspond to the
|
||||
* requested user. It will returns 0 for failure and 1 for success.
|
||||
*
|
||||
* For the moment ldaps is not supported. ldap serve can be on a
|
||||
* remote host.
|
||||
* ldaps is only supported for ldap_uri based connections.
|
||||
* ldap_cacertfile usually needs to be set for this to work.
|
||||
*
|
||||
* ldap serve can be on a remote host.
|
||||
*
|
||||
* You need the following parameters in you pam config:
|
||||
* ldapserver= OR ldap_uri=
|
||||
@ -202,10 +210,14 @@ free_out:
|
||||
* user_attr=
|
||||
* yubi_attr=
|
||||
*
|
||||
* If using ldap_uri, you can specify multiple failover hosts
|
||||
* eg.
|
||||
* ldap_uri=ldaps://host1.fqdn.example.com,ldaps://host2.fqdn.example.com
|
||||
*/
|
||||
static int
|
||||
authorize_user_token_ldap (struct cfg *cfg,
|
||||
const char *user,
|
||||
const char *password,
|
||||
const char *token_id)
|
||||
{
|
||||
int retval = 0;
|
||||
@ -221,15 +233,12 @@ authorize_user_token_ldap (struct cfg *cfg,
|
||||
struct berval **vals;
|
||||
int i, rc;
|
||||
|
||||
char *filter = NULL;
|
||||
char *find = NULL;
|
||||
int scope = LDAP_SCOPE_BASE;
|
||||
#endif
|
||||
DBG(("called"));
|
||||
#ifdef HAVE_LIBLDAP
|
||||
|
||||
if (cfg->user_attr == NULL) {
|
||||
DBG (("Trying to look up user to YubiKey mapping in LDAP, but user_attr not set!"));
|
||||
return 0;
|
||||
}
|
||||
if (cfg->yubi_attr == NULL) {
|
||||
DBG (("Trying to look up user to YubiKey mapping in LDAP, but yubi_attr not set!"));
|
||||
return 0;
|
||||
@ -245,7 +254,7 @@ authorize_user_token_ldap (struct cfg *cfg,
|
||||
rc = ldap_initialize (&ld, cfg->ldap_uri);
|
||||
if (rc != LDAP_SUCCESS)
|
||||
{
|
||||
DBG (("ldap_init: %s", ldap_err2string (rc)));
|
||||
DBG (("ldap_initialize: %s", ldap_err2string (rc)));
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
@ -261,11 +270,32 @@ authorize_user_token_ldap (struct cfg *cfg,
|
||||
}
|
||||
|
||||
/* LDAPv2 is historical -- RFC3494. */
|
||||
ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
|
||||
protocol = LDAP_VERSION3;
|
||||
ldap_set_option (ld, LDAP_OPT_PROTOCOL_VERSION, &protocol);
|
||||
|
||||
if (cfg->ldap_uri && cfg->ldap_cacertfile) {
|
||||
/* Set CA CERTFILE. This makes ldaps work when using ldap_uri */
|
||||
ldap_set_option (0, LDAP_OPT_X_TLS_CACERTFILE, cfg->ldap_cacertfile);
|
||||
}
|
||||
/* Bind anonymously to the LDAP server. */
|
||||
rc = ldap_simple_bind_s (ld, NULL, NULL);
|
||||
if (cfg->ldap_bind_user && cfg->ldap_bind_password) {
|
||||
DBG (("try bind with: %s:[%s]", cfg->ldap_bind_user, cfg->ldap_bind_password));
|
||||
rc = ldap_simple_bind_s (ld, cfg->ldap_bind_user, cfg->ldap_bind_password);
|
||||
} else if (cfg->ldap_bind_no_anonymous) {
|
||||
char *tmp_user;
|
||||
if (cfg->ldap_bind_user_filter) {
|
||||
tmp_user = filter_printf(cfg->ldap_bind_user_filter, user);
|
||||
} else {
|
||||
tmp_user = strdup(user);
|
||||
}
|
||||
DBG (("try bind with: %s:[XXXXX]", tmp_user, password));
|
||||
rc = ldap_simple_bind_s (ld, tmp_user, password);
|
||||
free(tmp_user);
|
||||
} else {
|
||||
DBG (("try bind anonymous"));
|
||||
rc = ldap_simple_bind_s (ld, NULL, NULL);
|
||||
}
|
||||
if (rc != LDAP_SUCCESS)
|
||||
{
|
||||
DBG (("ldap_simple_bind_s: %s", ldap_err2string (rc)));
|
||||
@ -274,22 +304,30 @@ authorize_user_token_ldap (struct cfg *cfg,
|
||||
}
|
||||
|
||||
/* Allocation of memory for search strings depending on input size */
|
||||
i = (strlen(cfg->user_attr) + strlen(cfg->ldapdn) + strlen(user) + 3) * sizeof(char);
|
||||
if ((find = malloc(i)) == NULL) {
|
||||
DBG (("Failed allocating %i bytes", i));
|
||||
retval = 0;
|
||||
goto done;
|
||||
if (cfg->user_attr && cfg->yubi_attr) {
|
||||
i = (strlen(cfg->user_attr) + strlen(cfg->ldapdn) + strlen(user) + 3) * sizeof(char);
|
||||
if ((find = malloc(i)) == NULL) {
|
||||
DBG (("Failed allocating %i bytes", i));
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
sprintf (find, "%s=%s,%s", cfg->user_attr, user, cfg->ldapdn);
|
||||
filter = NULL;
|
||||
} else {
|
||||
find = strdup(cfg->ldapdn); // allow free later-:)
|
||||
}
|
||||
if (cfg->ldap_filter) {
|
||||
filter = filter_printf(cfg->ldap_filter, user);
|
||||
scope = LDAP_SCOPE_SUBTREE;
|
||||
}
|
||||
|
||||
sprintf (find, "%s=%s,%s", cfg->user_attr, user, cfg->ldapdn);
|
||||
|
||||
attrs[0] = (char *) cfg->yubi_attr;
|
||||
|
||||
DBG(("LDAP : look up object '%s', ask for attribute '%s'", find, cfg->yubi_attr));
|
||||
DBG(("LDAP : look up object base='%s' filter='%s', ask for attribute '%s'", find,
|
||||
filter ? filter:"(null)", cfg->yubi_attr));
|
||||
|
||||
/* Search for the entry. */
|
||||
if ((rc = ldap_search_ext_s (ld, find, LDAP_SCOPE_BASE,
|
||||
NULL, attrs, 0, NULL, NULL, LDAP_NO_LIMIT,
|
||||
if ((rc = ldap_search_ext_s (ld, find, scope,
|
||||
filter, attrs, 0, NULL, NULL, LDAP_NO_LIMIT,
|
||||
LDAP_NO_LIMIT, &result)) != LDAP_SUCCESS)
|
||||
{
|
||||
DBG (("ldap_search_ext_s: %s", ldap_err2string (rc)));
|
||||
@ -313,16 +351,16 @@ authorize_user_token_ldap (struct cfg *cfg,
|
||||
{
|
||||
if ((vals = ldap_get_values_len (ld, e, a)) != NULL)
|
||||
{
|
||||
DBG(("LDAP : Found %i values - checking if any of them match '%s%s'",
|
||||
ldap_count_values_len(vals),
|
||||
cfg->yubi_attr_prefix ? cfg->yubi_attr_prefix : "",
|
||||
token_id));
|
||||
|
||||
yubi_attr_prefix_len = cfg->yubi_attr_prefix ? strlen(cfg->yubi_attr_prefix) : 0;
|
||||
|
||||
/* Compare each value for the attribute against the token id. */
|
||||
for (i = 0; vals[i] != NULL; i++)
|
||||
{
|
||||
DBG(("LDAP : Found %i values - checking if any of them match '%s:%s:%s'",
|
||||
ldap_count_values_len(vals),
|
||||
vals[i]->bv_val,
|
||||
cfg->yubi_attr_prefix ? cfg->yubi_attr_prefix : "", token_id));
|
||||
|
||||
/* Only values containing this prefix are considered. */
|
||||
if ((!cfg->yubi_attr_prefix || !strncmp (cfg->yubi_attr_prefix, vals[i]->bv_val, yubi_attr_prefix_len)))
|
||||
{
|
||||
@ -350,6 +388,8 @@ authorize_user_token_ldap (struct cfg *cfg,
|
||||
/* free memory allocated for search strings */
|
||||
if (find != NULL)
|
||||
free(find);
|
||||
if (filter != NULL)
|
||||
free(filter);
|
||||
|
||||
#else
|
||||
DBG (("Trying to use LDAP, but this function is not compiled in pam_yubico!!"));
|
||||
@ -672,6 +712,24 @@ parse_cfg (int flags, int argc, const char **argv, struct cfg *cfg)
|
||||
cfg->ldapserver = argv[i] + 11;
|
||||
if (strncmp (argv[i], "ldap_uri=", 9) == 0)
|
||||
cfg->ldap_uri = argv[i] + 9;
|
||||
if (strncmp (argv[i], "ldap_bind_no_anonymous", sizeof("ldap_bind_no_anonymous")-1) == 0)
|
||||
cfg->ldap_bind_no_anonymous = 1;
|
||||
if (strncmp (argv[i], "ldap_bind_user=", sizeof("ldap_bind_user=")-1) == 0)
|
||||
cfg->ldap_bind_user = argv[i] + sizeof("ldap_bind_user=")-1;
|
||||
if (strncmp (argv[i], "ldap_bind_user_filter=", sizeof("ldap_bind_user_filter=")-1) == 0)
|
||||
cfg->ldap_bind_user_filter = argv[i] + sizeof("ldap_bind_user_filter=")-1;
|
||||
if (strncmp (argv[i], "ldap_bind_password=", sizeof("ldap_bind_password=")-1) == 0)
|
||||
cfg->ldap_bind_password = argv[i] + sizeof("ldap_bind_password=")-1;
|
||||
if (strncmp (argv[i], "ldap_filter=", sizeof("ldap_filter=")-1) == 0)
|
||||
cfg->ldap_filter = argv[i] + sizeof("ldap_filter=")-1;
|
||||
if (strncmp (argv[i], "ldap_cacertfile=", sizeof("ldap_cacertfile=")-1) == 0)
|
||||
cfg->ldap_cacertfile = (char *) argv[i] + sizeof("ldap_cacertfile=")-1;
|
||||
/* compatible with https://github.com/Yubico/yubico-pam/pull/39/files */
|
||||
if (strncmp (argv[i], "binddn=", sizeof("binddn=")-1) == 0)
|
||||
cfg->ldap_bind_user = (char *) argv[i] + sizeof("binddn=")-1;
|
||||
if (strncmp (argv[i], "bindpw=", sizeof("bindpw=")-1) == 0)
|
||||
cfg->ldap_bind_password = (char *) argv[i] + sizeof("bindpw=")-1;
|
||||
/* compatible with https://github.com/Yubico/yubico-pam/pull/39/files */
|
||||
if (strncmp (argv[i], "ldapdn=", 7) == 0)
|
||||
cfg->ldapdn = argv[i] + 7;
|
||||
if (strncmp (argv[i], "user_attr=", 10) == 0)
|
||||
@ -706,6 +764,11 @@ parse_cfg (int flags, int argc, const char **argv, struct cfg *cfg)
|
||||
D (("authfile=%s", cfg->auth_file ? cfg->auth_file : "(null)"));
|
||||
D (("ldapserver=%s", cfg->ldapserver ? cfg->ldapserver : "(null)"));
|
||||
D (("ldap_uri=%s", cfg->ldap_uri ? cfg->ldap_uri : "(null)"));
|
||||
D (("ldap_bind_no_anonymous=%d", cfg->ldap_bind_no_anonymous));
|
||||
D (("ldap_bind_user=%s", cfg->ldap_bind_user ? cfg->ldap_bind_user : "(null)"));
|
||||
D (("ldap_bind_password=%s", cfg->ldap_bind_password ? cfg->ldap_bind_password : "(null)"));
|
||||
D (("ldap_filter=%s", cfg->ldap_filter ? cfg->ldap_filter : "(null)"));
|
||||
D (("ldap_cacertfile=%s", cfg->ldap_cacertfile ? cfg->ldap_cacertfile : "(null)"));
|
||||
D (("ldapdn=%s", cfg->ldapdn ? cfg->ldapdn : "(null)"));
|
||||
D (("user_attr=%s", cfg->user_attr ? cfg->user_attr : "(null)"));
|
||||
D (("yubi_attr=%s", cfg->yubi_attr ? cfg->yubi_attr : "(null)"));
|
||||
@ -742,6 +805,7 @@ pam_sm_authenticate (pam_handle_t * pamh,
|
||||
size_t templates = 0;
|
||||
char *urls[10];
|
||||
char *tmpurl = NULL;
|
||||
char *onlypasswd = NULL;
|
||||
|
||||
parse_cfg (flags, argc, argv, cfg);
|
||||
|
||||
@ -936,7 +1000,7 @@ pam_sm_authenticate (pam_handle_t * pamh,
|
||||
/* user entered their system password followed by generated OTP? */
|
||||
if (password_len > TOKEN_OTP_LEN + cfg->token_id_length)
|
||||
{
|
||||
char *onlypasswd = strdup (password);
|
||||
onlypasswd = strdup (password);
|
||||
|
||||
if (! onlypasswd) {
|
||||
retval = PAM_BUF_ERR;
|
||||
@ -949,7 +1013,6 @@ pam_sm_authenticate (pam_handle_t * pamh,
|
||||
"setting item PAM_AUTHTOK"));
|
||||
|
||||
retval = pam_set_item (pamh, PAM_AUTHTOK, onlypasswd);
|
||||
free (onlypasswd);
|
||||
if (retval != PAM_SUCCESS)
|
||||
{
|
||||
DBG (("set_item returned error: %s", pam_strerror (pamh, retval)));
|
||||
@ -981,7 +1044,7 @@ pam_sm_authenticate (pam_handle_t * pamh,
|
||||
|
||||
/* authorize the user with supplied token id */
|
||||
if (cfg->ldapserver != NULL || cfg->ldap_uri != NULL)
|
||||
valid_token = authorize_user_token_ldap (cfg, user, otp_id);
|
||||
valid_token = authorize_user_token_ldap (cfg, user, onlypasswd, otp_id);
|
||||
else
|
||||
valid_token = authorize_user_token (cfg, user, otp_id, pamh);
|
||||
|
||||
@ -1008,6 +1071,8 @@ pam_sm_authenticate (pam_handle_t * pamh,
|
||||
}
|
||||
|
||||
done:
|
||||
if (onlypasswd)
|
||||
free(onlypasswd);
|
||||
if (templates > 0)
|
||||
{
|
||||
size_t i;
|
||||
@ -1029,7 +1094,8 @@ done:
|
||||
retval = PAM_SUCCESS;
|
||||
}
|
||||
DBG (("done. [%s]", pam_strerror (pamh, retval)));
|
||||
pam_set_data (pamh, "yubico_setcred_return", (void*) (intptr_t) retval, NULL);
|
||||
pam_set_data (pamh, "yubico_setcred_return", (void*)(intptr_t)retval, NULL);
|
||||
pam_set_data (pamh, "yubico_used_ldap", (void*)(intptr_t)cfg->ldap_bind_no_anonymous, NULL);
|
||||
|
||||
if (resp)
|
||||
{
|
||||
@ -1047,16 +1113,59 @@ pam_sm_setcred (pam_handle_t * pamh, int flags, int argc, const char **argv)
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
|
||||
{
|
||||
int use_ldap = -1;
|
||||
int rc = pam_get_data(pamh, "yubico_used_ldap", (const void**)&use_ldap);
|
||||
if (rc == PAM_SUCCESS && use_ldap) {
|
||||
int retval;
|
||||
rc = pam_get_data(pamh, "yubico_setcred_return", (const void**)&retval);
|
||||
if (rc == PAM_SUCCESS && retval == PAM_SUCCESS) {
|
||||
D (("pam_sm_acct_mgmt returing PAM_SUCCESS"));
|
||||
return PAM_SUCCESS;
|
||||
}
|
||||
}
|
||||
D (("pam_sm_acct_mgmt returing PAM_AUTH_ERR:%d", use_ldap));
|
||||
return PAM_AUTH_ERR;
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_open_session(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
|
||||
D(("pam_sm_open_session"));
|
||||
return (PAM_SUCCESS);
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_close_session(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
D(("pam_sm_close_session"));
|
||||
return (PAM_SUCCESS);
|
||||
}
|
||||
|
||||
PAM_EXTERN int
|
||||
pam_sm_chauthtok(pam_handle_t *pamh, int flags,
|
||||
int argc, const char *argv[])
|
||||
{
|
||||
D(("pam_sm_chauthtok"));
|
||||
return (PAM_SERVICE_ERR);
|
||||
}
|
||||
|
||||
|
||||
#ifdef PAM_STATIC
|
||||
|
||||
struct pam_module _pam_yubico_modstruct = {
|
||||
"pam_yubico",
|
||||
pam_sm_authenticate,
|
||||
pam_sm_setcred,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
pam_sm_acct_mgmt,
|
||||
pam_sm_open_session,
|
||||
pam_sm_close_session,
|
||||
pam_sm_chauthtok
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -125,7 +125,27 @@ static void test_load_chalresp_state(void) {
|
||||
|
||||
#endif /* HAVE_CR */
|
||||
|
||||
static void test_filter_printf(void) {
|
||||
assert(filter_result_len("meno %u", "doof", NULL) == 9);
|
||||
assert(filter_result_len("meno %u %u", "doof", NULL) == 14);
|
||||
assert(filter_result_len("%u meno %u", "doof", NULL) == 14);
|
||||
assert(filter_result_len("%u me %u no %u", "doof", NULL) == 20);
|
||||
assert(filter_result_len("meno %w %%u", "doof", NULL) == 13);
|
||||
assert(filter_result_len("meno %w %%u meno", "doof", NULL) == 18);
|
||||
assert(filter_result_len("meno ", "doof", NULL) == 5);
|
||||
|
||||
assert(!strcmp(filter_printf("meno %u", "doof"), "meno doof"));
|
||||
assert(!strcmp(filter_printf("meno %u %u", "doof"), "meno doof doof"));
|
||||
assert(!strcmp(filter_printf("%u meno %u", "doof"), "doof meno doof"));
|
||||
assert(!strcmp(filter_printf("%u me %u no %u", "doof"), "doof me doof no doof"));
|
||||
assert(!strcmp(filter_printf("meno %w %%u", "doof"), "meno %w %doof"));
|
||||
assert(!strcmp(filter_printf("meno %w %%u meno", "doof"), "meno %w %doof meno"));
|
||||
assert(!strcmp(filter_printf("meno ", "doof"), "meno "));
|
||||
printf("test_filter_printf OK\n");
|
||||
}
|
||||
|
||||
int main (void) {
|
||||
test_filter_printf();
|
||||
test_get_user_cfgfile_path();
|
||||
test_check_user_token();
|
||||
#if HAVE_CR
|
||||
|
46
util.c
46
util.c
@ -35,6 +35,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -461,3 +464,46 @@ write_chalresp_state(FILE *f, CR_STATE *state)
|
||||
return 0;
|
||||
}
|
||||
#endif /* HAVE_CR */
|
||||
|
||||
|
||||
int filter_result_len(const char *filter, const char *user, char *output) {
|
||||
int user_len = strlen(user);
|
||||
int filter_len = strlen(filter);
|
||||
const char *result;
|
||||
int result_len = 0;
|
||||
const char *percent_sign;
|
||||
for (result = filter ; (percent_sign = strchr(result, '%')) ; result = percent_sign) {
|
||||
if ((percent_sign + 1 - filter) > filter_len) {
|
||||
break;
|
||||
}
|
||||
if (output) {
|
||||
memcpy(output, result, percent_sign - result);
|
||||
output += percent_sign - result;
|
||||
}
|
||||
if (*(percent_sign+1) == 'u') {
|
||||
if (output) {
|
||||
memcpy(output, user, user_len);
|
||||
output += user_len;
|
||||
}
|
||||
result_len += (percent_sign - result) + user_len;
|
||||
++percent_sign; // skip u
|
||||
} else {
|
||||
if (output) {
|
||||
*output++ = '%';
|
||||
}
|
||||
result_len += percent_sign + 1 - result;
|
||||
}
|
||||
++percent_sign;
|
||||
}
|
||||
if (output) {
|
||||
memcpy(output, result, ((filter+filter_len)-result) + 1);
|
||||
}
|
||||
return result_len + (filter+filter_len-result);
|
||||
}
|
||||
|
||||
char *filter_printf(const char *filter, const char *user) {
|
||||
char *result = malloc(filter_result_len(filter, user, NULL) + 1);
|
||||
filter_result_len(filter, user, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user