Review #748 » 0001-Add-SASL-authentication-support-EXTERNAL-PLAIN.patch
AUTHORS | ||
---|---|---|
ack|, ato, blackmore, lafouine, Gaston & gromit
|
||
Crypto shamelessly stolen from Christophe 'sexy' Devine.
|
||
Credits to Jouni Malinen for base64 library (http://web.mit.edu/freebsd/head/contrib/wpa/src/utils/)
|
bip.conf.5 | ||
---|---|---|
upon connect. If not specified and if \fBdefault_realname\fP is specified in
|
||
the \fBuser\fP section, BIP will use that default realname string.
|
||
.TP
|
||
\fBsasl_mechanism\fP
|
||
Tells BIP to use specified SASL mechanism. Currently supported: PLAIN, EXTERNAL.
|
||
PLAIN mechanism requires \fBsasl_username\fP and \fBsasl_password\fP and is the
|
||
default if these are set.
|
||
.TP
|
||
\fBsasl_username\fP
|
||
This connection's username to pass on using SASL authentication.
|
||
.TP
|
||
\fBsasl_password\fP
|
||
This connection's password to pass on using SASL authentication.
|
||
.TP
|
||
\fBsource_port\fP
|
||
If specified, tells BIP to connect from this port to the IRC server.
|
samples/bip.conf | ||
---|---|---|
#You can specify this field more than once. BIP will send the text as is to the server.
|
||
#on_connect_send = "PRIVMSG NickServ :IDENTIFY nspassword";
|
||
# You can connect with SASL on networks supporting it
|
||
#sasl_username = "username";
|
||
#sasl_password = "sikioure password";
|
||
#sasl_mechanism = "PLAIN";
|
||
# Some options:
|
||
#away_nick = "bip`away";
|
||
# Away message to be set when no client is connected
|
samples/bip.vim | ||
---|---|---|
\ follow_nick ignore_first_nick log ignore_server_capab
|
||
syn keyword bipCoKeyword contained nextgroup=bipStringV name user nick
|
||
\ network password vhost away_nick on_connect_send realname
|
||
\ no_client_away_msg ssl_check_mode
|
||
\ no_client_away_msg ssl_check_mode sasl_username sasl_password
|
||
\ sasl_mechanism
|
||
syn keyword bipCoKeyword contained nextgroup=bipNumericV source_port
|
||
" Channel elements (lvl 2)
|
src/Makefile.am | ||
---|---|---|
md5.c md5.h \
|
||
path_util.c path_util.h \
|
||
tuple.h \
|
||
util.c util.h
|
||
util.c util.h \
|
||
utils/base64.c utils/base64.h
|
||
libbip_a_CFLAGS = ${OPENSSL_CFLAGS} $(AM_CFLAGS)
|
||
src/bip.c | ||
---|---|---|
case LEX_PASSWORD:
|
||
MOVE_STRING(l->s_password, t->pdata);
|
||
break;
|
||
case LEX_SASL_USERNAME:
|
||
MOVE_STRING(l->sasl_username, t->pdata);
|
||
break;
|
||
case LEX_SASL_PASSWORD:
|
||
MOVE_STRING(l->sasl_password, t->pdata);
|
||
break;
|
||
case LEX_SASL_MECHANISM:
|
||
if (strcmp(t->pdata, "PLAIN") == 0) {
|
||
l->sasl_mechanism = SASL_AUTH_PLAIN;
|
||
} else if (strcmp(t->pdata, "EXTERNAL") == 0) {
|
||
l->sasl_mechanism = SASL_AUTH_EXTERNAL;
|
||
} else {
|
||
conf_die(bip, "Unsupported SASL mechanism %s.", t->pdata);
|
||
return 0;
|
||
}
|
||
break;
|
||
case LEX_VHOST:
|
||
MOVE_STRING(l->vhost, t->pdata);
|
||
break;
|
||
... | ... | |
l->realname = bip_strdup(user->default_realname);
|
||
}
|
||
if (l->sasl_username && !l->sasl_password) {
|
||
conf_die(bip, "sasl_username set without sasl_password.");
|
||
return 0;
|
||
}
|
||
if (!l->sasl_username && l->sasl_password) {
|
||
conf_die(bip, "sasl_password set without sasl_username.");
|
||
return 0;
|
||
}
|
||
if (l->sasl_mechanism == SASL_AUTH_PLAIN && (!l->sasl_username || !l->sasl_password)) {
|
||
conf_die(bip, "SASL mechanism PLAIN requires username and password.");
|
||
return 0;
|
||
}
|
||
if (l->sasl_username && !l->sasl_mechanism)
|
||
l->sasl_mechanism = SASL_AUTH_PLAIN;
|
||
l->in_use = 1;
|
||
return 1;
|
||
}
|
src/conf.y | ||
---|---|---|
%}
|
||
%token LEX_IP LEX_EQ LEX_PORT LEX_CSS LEX_SEMICOLON LEX_CONNECTION LEX_NETWORK LEX_LBRA LEX_RBRA LEX_USER LEX_NAME LEX_NICK LEX_SERVER LEX_PASSWORD LEX_SRCIP LEX_HOST LEX_VHOST LEX_SOURCE_PORT LEX_NONE LEX_COMMENT LEX_BUNCH LEX_REALNAME LEX_SSL LEX_SSL_CHECK_MODE LEX_SSL_CHECK_STORE LEX_SSL_CLIENT_CERTFILE LEX_CIPHERS LEX_CSS_CIPHERS LEX_DEFAULT_CIPHERS LEX_DH_PARAM LEX_CHANNEL LEX_KEY LEX_LOG_ROOT LEX_LOG_FORMAT LEX_LOG_LEVEL LEX_BACKLOG_LINES LEX_BACKLOG_TIMESTAMP LEX_BACKLOG_NO_TIMESTAMP LEX_BACKLOG LEX_LOG LEX_LOG_SYSTEM LEX_LOG_SYNC_INTERVAL LEX_FOLLOW_NICK LEX_ON_CONNECT_SEND LEX_AWAY_NICK LEX_PID_FILE LEX_WRITE_OIDENTD LEX_OIDENTD_FILE LEX_IGN_FIRST_NICK LEX_ALWAYS_BACKLOG LEX_BLRESET_ON_TALK LEX_BLRESET_CONNECTION LEX_DEFAULT_USER LEX_DEFAULT_NICK LEX_DEFAULT_REALNAME LEX_NO_CLIENT_AWAY_MSG LEX_BL_MSG_ONLY LEX_ADMIN LEX_BIP_USE_NOTICE LEX_CSS_PEM LEX_AUTOJOIN_ON_KICK LEX_IGNORE_CAPAB LEX_RECONN_TIMER
|
||
%token LEX_IP LEX_EQ LEX_PORT LEX_CSS LEX_SEMICOLON LEX_CONNECTION LEX_NETWORK LEX_LBRA LEX_RBRA LEX_USER LEX_NAME LEX_NICK LEX_SERVER LEX_PASSWORD LEX_SRCIP LEX_HOST LEX_VHOST LEX_SOURCE_PORT LEX_NONE LEX_COMMENT LEX_BUNCH LEX_REALNAME LEX_SSL LEX_SSL_CHECK_MODE LEX_SSL_CHECK_STORE LEX_SSL_CLIENT_CERTFILE LEX_CIPHERS LEX_CSS_CIPHERS LEX_DEFAULT_CIPHERS LEX_DH_PARAM LEX_CHANNEL LEX_KEY LEX_LOG_ROOT LEX_LOG_FORMAT LEX_LOG_LEVEL LEX_BACKLOG_LINES LEX_BACKLOG_TIMESTAMP LEX_BACKLOG_NO_TIMESTAMP LEX_BACKLOG LEX_LOG LEX_LOG_SYSTEM LEX_LOG_SYNC_INTERVAL LEX_FOLLOW_NICK LEX_ON_CONNECT_SEND LEX_AWAY_NICK LEX_PID_FILE LEX_WRITE_OIDENTD LEX_OIDENTD_FILE LEX_IGN_FIRST_NICK LEX_ALWAYS_BACKLOG LEX_BLRESET_ON_TALK LEX_BLRESET_CONNECTION LEX_DEFAULT_USER LEX_DEFAULT_NICK LEX_DEFAULT_REALNAME LEX_NO_CLIENT_AWAY_MSG LEX_BL_MSG_ONLY LEX_ADMIN LEX_BIP_USE_NOTICE LEX_CSS_PEM LEX_AUTOJOIN_ON_KICK LEX_IGNORE_CAPAB LEX_RECONN_TIMER LEX_SASL_USERNAME LEX_SASL_PASSWORD LEX_SASL_MECHANISM
|
||
%union {
|
||
int number;
|
||
... | ... | |
$3); }
|
||
| LEX_PASSWORD LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_PASSWORD,
|
||
$3); }
|
||
| LEX_SASL_USERNAME LEX_EQ LEX_STRING { $$ = tuple_s_new(
|
||
LEX_SASL_USERNAME, $3); }
|
||
| LEX_SASL_PASSWORD LEX_EQ LEX_STRING { $$ = tuple_s_new(
|
||
LEX_SASL_PASSWORD, $3); }
|
||
| LEX_SASL_MECHANISM LEX_EQ LEX_STRING { $$ = tuple_s_new(
|
||
LEX_SASL_MECHANISM, $3); }
|
||
| LEX_VHOST LEX_EQ LEX_STRING { $$ = tuple_s_new(LEX_VHOST, $3); }
|
||
| LEX_SOURCE_PORT LEX_EQ LEX_INT {
|
||
$$ = tuple_i_new(LEX_SOURCE_PORT, $3); }
|
src/irc.c | ||
---|---|---|
#include "log.h"
|
||
#include "connection.h"
|
||
#include "md5.h"
|
||
#include "utils/base64.h"
|
||
#define S_CONN_DELAY (10)
|
||
... | ... | |
static void irc_cli_make_join(struct link_client *ic);
|
||
static void server_setup_reconnect_timer(struct link *link);
|
||
int irc_cli_bip(bip_t *bip, struct link_client *ic, struct line *line);
|
||
static int irc_server_sasl_authenticate(struct link_server *ircs);
|
||
static char *sasl_mechanism_to_text(int sasl_mechanism);
|
||
#define LAGOUT_TIME 480
|
||
#define LAGCHECK_TIME (90)
|
||
... | ... | |
}
|
||
ret = OK_FORGET;
|
||
}
|
||
} else if (irc_line_elem_equals(line, 0, "CAP")) {
|
||
if (LINK(server)->sasl_mechanism) {
|
||
if (irc_line_elem_equals(line, 2, "ACK") && irc_line_elem_equals(line, 3, "sasl")) {
|
||
// Server is answering our CAP REQ :sasl and is SASL capable
|
||
char *sasl_mech = sasl_mechanism_to_text(LINK(server)->sasl_mechanism);
|
||
mylog(LOG_INFO, "[%s] Server is SASL capable, starting %s authentication.",
|
||
LINK(server)->name, sasl_mech);
|
||
WRITE_LINE1(CONN(server), NULL, "AUTHENTICATE", sasl_mech);
|
||
ret = OK_FORGET;
|
||
} else if (irc_line_elem_equals(line, 2, "NAK") && irc_line_elem_equals(line, 3, "sasl")) {
|
||
// Server is answering our CAP REQ :sasl and isn't SASL capable
|
||
mylog(LOG_INFO, "[%s] Server is not SASL capable.", LINK(server)->name);
|
||
ret = ERR_PROTOCOL;
|
||
} else {
|
||
// Unhandled CAP message
|
||
mylog(LOG_ERROR, "[%s] Unhandled CAP message: %s",
|
||
LINK(server)->name, irc_line_to_string(line));
|
||
ret = OK_FORGET;
|
||
}
|
||
} else {
|
||
// Unhandled CAP message
|
||
mylog(LOG_ERROR, "[%s] Unhandled CAP message: %s", LINK(server)->name,
|
||
irc_line_to_string(line));
|
||
ret = OK_FORGET;
|
||
}
|
||
} else if (irc_line_elem_equals(line, 0, "AUTHENTICATE")) {
|
||
if (LINK(server)->sasl_mechanism) {
|
||
if (irc_line_count(line) == 2 && irc_line_elem_equals(line, 1, "+")) {
|
||
// Server is waiting for us to authenticate, let's do it
|
||
mylog(LOG_INFO, "[%s] Server accepted our authentication mechanism.",
|
||
LINK(server)->name);
|
||
ret = irc_server_sasl_authenticate(server);
|
||
} else {
|
||
// Anything else than "AUTHENTICATE +" is unknown to us
|
||
mylog(LOG_ERROR, "[%s] Server sent gibberish: %s",
|
||
LINK(server)->name, irc_line_to_string(line));
|
||
ret = ERR_PROTOCOL;
|
||
}
|
||
} else {
|
||
// Unhandled AUTHENTICATE message
|
||
mylog(LOG_ERROR, "[%s] Unhandled AUTHENTICATE message: %s",
|
||
LINK(server)->name, irc_line_to_string(line));
|
||
ret = OK_FORGET;
|
||
}
|
||
} else if (irc_line_elem_equals(line, 0, "900")) {
|
||
if (irc_line_count(line) >= 5) {
|
||
mylog(LOG_INFO, "[%s] Logged in as %s(%s): %s", LINK(server)->name,
|
||
irc_line_elem(line, 3), irc_line_elem(line, 2), irc_line_elem(line, 4));
|
||
} else {
|
||
mylog(LOG_INFO, "[%s] Logged in: %s", LINK(server)->name, irc_line_to_string(line));
|
||
}
|
||
ret = OK_FORGET;
|
||
} else if (irc_line_elem_equals(line, 0, "901")) {
|
||
if (irc_line_count(line) >= 4) {
|
||
mylog(LOG_INFO, "[%s] Logged out: %s",
|
||
LINK(server)->name, irc_line_elem(line, 3));
|
||
} else {
|
||
mylog(LOG_INFO, "[%s] Logged out: %s", LINK(server)->name, irc_line_to_string(line));
|
||
}
|
||
ret = OK_FORGET;
|
||
} else if (irc_line_elem_equals(line, 0, "902")) {
|
||
mylog(LOG_INFO, "[%s] Account unavailable: %s",
|
||
LINK(server)->name, irc_line_to_string(line));
|
||
ret = OK_FORGET;
|
||
} else if (irc_line_elem_equals(line, 0, "903")) {
|
||
mylog(LOG_INFO, "[%s] SASL authentication successful", LINK(server)->name);
|
||
WRITE_LINE1(CONN(server), NULL, "CAP", "END");
|
||
ret = OK_FORGET;
|
||
} else if (irc_line_elem_equals(line, 0, "904")) {
|
||
mylog(LOG_ERROR, "[%s] SASL authentication failed", LINK(server)->name);
|
||
ret = ERR_AUTH;
|
||
} else if (irc_line_elem_equals(line, 0, "905")) {
|
||
mylog(LOG_ERROR, "[%s] SASL message too long", LINK(server)->name);
|
||
ret = ERR_AUTH;
|
||
} else if (irc_line_elem_equals(line, 0, "906")) {
|
||
mylog(LOG_ERROR, "[%s] SASL authentication aborted by client",
|
||
LINK(server)->name);
|
||
ret = ERR_AUTH;
|
||
} else if (irc_line_elem_equals(line, 0, "907")) {
|
||
mylog(LOG_ERROR, "[%s] SASL authentication has already been completed",
|
||
LINK(server)->name);
|
||
ret = OK_FORGET;
|
||
} else if (irc_line_elem_equals(line, 0, "908")) {
|
||
mylog(LOG_ERROR, "[%s] Server only accepts following authentication mechanisms: %s",
|
||
LINK(server)->name, irc_line_elem(line, 2));
|
||
ret = ERR_AUTH;
|
||
} else if (irc_line_elem_equals(line, 0, "433")) {
|
||
if (LINK(server)->s_state != IRCS_CONNECTED) {
|
||
size_t nicklen = strlen(server->nick);
|
||
... | ... | |
#endif
|
||
}
|
||
static char *sasl_mechanism_to_text(int sasl_mechanism)
|
||
{
|
||
switch (sasl_mechanism) {
|
||
case SASL_AUTH_EXTERNAL:
|
||
return "EXTERNAL";
|
||
case SASL_AUTH_PLAIN:
|
||
return "PLAIN";
|
||
default:
|
||
return "UNKOWN_MECHANISM";
|
||
}
|
||
}
|
||
// Per RFC send packets of max 400 chars at a time
|
||
#define SASL_AUTH_CHUNK_SZ 400
|
||
static int irc_server_sasl_authenticate(struct link_server *ircs)
|
||
{
|
||
char *sasl_username = LINK(ircs)->sasl_username;
|
||
char *sasl_password = LINK(ircs)->sasl_password;
|
||
if (LINK(ircs)->sasl_mechanism == SASL_AUTH_EXTERNAL) {
|
||
WRITE_LINE1(CONN(ircs), NULL, "AUTHENTICATE", "+");
|
||
return OK_FORGET;
|
||
}
|
||
// Should not happen, but we never know right ?
|
||
if (!sasl_username || !sasl_password) {
|
||
mylog(LOG_ERROR, "[%s] Missing SASL username or password.", LINK(ircs)->name);
|
||
return ERR_AUTH;
|
||
}
|
||
/*
|
||
* Other than EXTERNAL we only support PLAIN.
|
||
*/
|
||
size_t chunk_chars = SASL_AUTH_CHUNK_SZ;
|
||
char chunk[SASL_AUTH_CHUNK_SZ + 1];
|
||
size_t u_len = strlen(sasl_username);
|
||
size_t p_len = strlen(sasl_password);
|
||
size_t raw_len = u_len*2 + p_len + 2;
|
||
size_t enc_len;
|
||
unsigned char *raw_str = bip_malloc(raw_len + 1);
|
||
unsigned char *enc_str;
|
||
memcpy(raw_str, sasl_username, u_len);
|
||
raw_str[u_len] = '\0';
|
||
memcpy(raw_str + u_len + 1, sasl_username, u_len);
|
||
raw_str[u_len*2 + 1] = '\0';
|
||
memcpy(raw_str + u_len*2 + 2, sasl_password, p_len);
|
||
enc_str = base64_encode(raw_str, raw_len, &enc_len);
|
||
mylog(LOG_DEBUG, "[%s] Base64 encoded SASL auth token (len %d): %s", LINK(ircs)->name, enc_len, enc_str);
|
||
for (size_t i = 0; i < enc_len; i += chunk_chars) {
|
||
size_t remaining = enc_len - i;
|
||
if (remaining < chunk_chars) {
|
||
memcpy(chunk, &enc_str[i], remaining);
|
||
chunk[remaining]= '\0';
|
||
} else {
|
||
memcpy(chunk, &enc_str[i], chunk_chars);
|
||
chunk[chunk_chars]= '\0';
|
||
}
|
||
mylog(LOG_DEBUG, "[%s] SASL AUTHENTICATE chunk %d, len %d: %s",
|
||
LINK(ircs)->name, i/chunk_chars, strlen(chunk), chunk);
|
||
WRITE_LINE1(CONN(ircs), NULL, "AUTHENTICATE", chunk);
|
||
// Send a closing AUTHENTICATE line if last chunk size was exactly 400
|
||
if (remaining == chunk_chars) {
|
||
mylog(LOG_DEBUG, "[%s] Last SASL chunk was exactly 400, sending +",
|
||
LINK(ircs)->name);
|
||
WRITE_LINE1(CONN(ircs), NULL, "AUTHENTICATE", "+");
|
||
break;
|
||
}
|
||
}
|
||
free(enc_str);
|
||
return OK_FORGET;
|
||
}
|
||
static void irc_server_startup(struct link_server *ircs)
|
||
{
|
||
char *nick;
|
||
... | ... | |
list_add_last(&_bip->conn_list, conn);
|
||
oidentd_dump(_bip);
|
||
if (link->sasl_mechanism) {
|
||
mylog(LOG_INFO, "[%s] SASL (%s) enabled, sending CAP REQ.",
|
||
link->name, sasl_mechanism_to_text(link->sasl_mechanism));
|
||
WRITE_LINE2(conn, NULL, "CAP", "REQ", ":sasl");
|
||
}
|
||
irc_server_startup(ls);
|
||
return conn;
|
||
}
|
||
... | ... | |
MAYFREE(link->username);
|
||
MAYFREE(link->realname);
|
||
MAYFREE(link->s_password);
|
||
MAYFREE(link->sasl_username);
|
||
MAYFREE(link->sasl_password);
|
||
MAYFREE(link->connect_nick);
|
||
MAYFREE(link->vhost);
|
||
#ifdef HAVE_LIBSSL
|
src/irc.h | ||
---|---|---|
struct server *serverv;
|
||
};
|
||
#define SASL_AUTH_EXTERNAL 1
|
||
#define SASL_AUTH_PLAIN 2
|
||
struct link {
|
||
char *name; /* id */
|
||
... | ... | |
char *username;
|
||
char *realname;
|
||
char *s_password;
|
||
char *sasl_username;
|
||
char *sasl_password;
|
||
int sasl_mechanism;
|
||
char *connect_nick;
|
||
/* socket creation info */
|
src/lex.l | ||
---|---|---|
"client_side_ssl_pem" { return LEX_CSS_PEM; }
|
||
"client_side_ciphers" { return LEX_CSS_CIPHERS; }
|
||
"client_side_dh_param" { return LEX_DH_PARAM; }
|
||
"sasl_username" { return LEX_SASL_USERNAME; }
|
||
"sasl_password" { return LEX_SASL_PASSWORD; }
|
||
"sasl_mechanism" { return LEX_SASL_MECHANISM; }
|
||
"ignore_server_capab" { return LEX_IGNORE_CAPAB; }
|
||
"reconn_timer" { return LEX_RECONN_TIMER; }
|
||
\"[^"]*\" {
|
src/utils/base64.c | ||
---|---|---|
/*
|
||
* Base64 encoding/decoding (RFC1341)
|
||
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
|
||
*
|
||
* This software may be distributed under the terms of the BSD license.
|
||
* See README for more details.
|
||
*/
|
||
#include <stddef.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include "base64.h"
|
||
static const unsigned char base64_table[65] =
|
||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||
/**
|
||
* base64_encode - Base64 encode
|
||
* @src: Data to be encoded
|
||
* @len: Length of the data to be encoded
|
||
* @out_len: Pointer to output length variable, or %NULL if not used
|
||
* Returns: Allocated buffer of out_len bytes of encoded data,
|
||
* or %NULL on failure
|
||
*
|
||
* Caller is responsible for freeing the returned buffer. Returned buffer is
|
||
* nul terminated to make it easier to use as a C string. The nul terminator is
|
||
* not included in out_len.
|
||
*
|
||
* BIP change: remove line returns.
|
||
*/
|
||
unsigned char * base64_encode(const unsigned char *src, size_t len,
|
||
size_t *out_len)
|
||
{
|
||
unsigned char *out, *pos;
|
||
const unsigned char *end, *in;
|
||
size_t olen;
|
||
int line_len;
|
||
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
|
||
olen += olen / 72; /* line feeds */
|
||
olen++; /* nul termination */
|
||
if (olen < len)
|
||
return NULL; /* integer overflow */
|
||
out = malloc(olen);
|
||
if (out == NULL)
|
||
return NULL;
|
||
end = src + len;
|
||
in = src;
|
||
pos = out;
|
||
line_len = 0;
|
||
while (end - in >= 3) {
|
||
*pos++ = base64_table[in[0] >> 2];
|
||
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
|
||
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
|
||
*pos++ = base64_table[in[2] & 0x3f];
|
||
in += 3;
|
||
line_len += 4;
|
||
/*
|
||
* BIP change: remove line returns.
|
||
if (line_len >= 72) {
|
||
*pos++ = '\n';
|
||
line_len = 0;
|
||
}
|
||
*/
|
||
}
|
||
if (end - in) {
|
||
*pos++ = base64_table[in[0] >> 2];
|
||
if (end - in == 1) {
|
||
*pos++ = base64_table[(in[0] & 0x03) << 4];
|
||
*pos++ = '=';
|
||
} else {
|
||
*pos++ = base64_table[((in[0] & 0x03) << 4) |
|
||
(in[1] >> 4)];
|
||
*pos++ = base64_table[(in[1] & 0x0f) << 2];
|
||
}
|
||
*pos++ = '=';
|
||
line_len += 4;
|
||
}
|
||
/*
|
||
* BIP change: remove line returns.
|
||
if (line_len)
|
||
*pos++ = '\n';
|
||
*/
|
||
*pos = '\0';
|
||
if (out_len)
|
||
*out_len = (size_t)(pos - out);
|
||
return out;
|
||
}
|
||
src/utils/base64.h | ||
---|---|---|
/*
|
||
* Base64 encoding/decoding (RFC1341)
|
||
* Copyright (c) 2005, Jouni Malinen <j@w1.fi>
|
||
*
|
||
* This software may be distributed under the terms of the BSD license.
|
||
* See README for more details.
|
||
*/
|
||
#ifndef BASE64_H
|
||
#define BASE64_H
|
||
unsigned char * base64_encode(const unsigned char *src, size_t len,
|
||
size_t *out_len);
|
||
#endif /* BASE64_H */
|