mirror of
https://github.com/asterisk/asterisk.git
synced 2025-08-22 13:38:18 +00:00
Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e56a90568d | ||
|
0adbc4f499 | ||
|
91be429a41 | ||
|
d3caebe072 | ||
|
fda68af0f6 | ||
|
a3838678f4 | ||
|
b9f026c7fd | ||
|
4857b7917a | ||
|
cabd2d6950 | ||
|
8ed1eeeaa6 | ||
|
01695f4507 | ||
|
c4d6abf01e | ||
|
f63cd633de | ||
|
0740db7608 |
1
.lastclean
Normal file
1
.lastclean
Normal file
@@ -0,0 +1 @@
|
||||
40
|
12
CHANGES
12
CHANGES
@@ -12,6 +12,18 @@
|
||||
===
|
||||
==============================================================================
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 19.3.1 to Asterisk 19.3.2 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
func_odbc
|
||||
------------------
|
||||
* A SQL_ESC_BACKSLASHES dialplan function has been added which
|
||||
escapes backslashes. Usage of this is dependent on whether the
|
||||
database in use can use backslashes to escape ticks or not. If
|
||||
it can, then usage of this prevents a broken SQL query depending
|
||||
on how the SQL query is constructed.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
--- Functionality changes from Asterisk 19.2.0 to Asterisk 19.3.0 ------------
|
||||
------------------------------------------------------------------------------
|
||||
|
9
Makefile
9
Makefile
@@ -101,6 +101,11 @@ export TAR
|
||||
export PATCH
|
||||
export SED
|
||||
export NM
|
||||
export FIND
|
||||
export BASENAME
|
||||
export DIRNAME
|
||||
export XMLLINT
|
||||
export XMLSTARLET
|
||||
|
||||
# makeopts is required unless the goal is just {dist{-}}clean
|
||||
ifeq ($(MAKECMDGOALS),clean)
|
||||
@@ -480,7 +485,7 @@ XML_core_en_US := $(shell build_tools/make_xml_documentation --command=print_dep
|
||||
# core-en_US.xml is the normal documentation created with asterisk builds.
|
||||
doc/core-en_US.xml: makeopts .lastclean $(XML_core_en_US)
|
||||
@build_tools/make_xml_documentation --command=create_xml --source-tree=. --mod-subdirs="$(DOC_MOD_SUBDIRS)" \
|
||||
--with-moduleinfo --validate --output-file=$@
|
||||
--with-moduleinfo --output-file=$@
|
||||
|
||||
# The full-en_US.xml target is only called by the wiki documentation generation process
|
||||
# and does special post-processing in preparation for uploading to the wiki.
|
||||
@@ -492,7 +497,7 @@ ifeq ($(PYTHON),:)
|
||||
@echo "--------------------------------------------------------------------------"
|
||||
else
|
||||
@build_tools/make_xml_documentation --command=create_xml --source-tree=. --mod-subdirs="$(DOC_MOD_SUBDIRS)" \
|
||||
--for-wiki --validate --output-file=$@ --core-output-file=./doc/core-en_US.xml
|
||||
--for-wiki --output-file=$@ --core-output-file=./doc/core-en_US.xml
|
||||
endif
|
||||
|
||||
validate-docs: doc/core-en_US.xml
|
||||
|
11
asterisk-19.3.3-summary.html
Normal file
11
asterisk-19.3.3-summary.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><title>Release Summary - asterisk-19.3.3</title><h1 align="center"><a name="top">Release Summary</a></h1><h3 align="center">asterisk-19.3.3</h3><h3 align="center">Date: 2022-04-26</h3><h3 align="center"><asteriskteam@digium.com></h3><hr><h2 align="center">Table of Contents</h2><ol>
|
||||
<li><a href="#summary">Summary</a></li>
|
||||
<li><a href="#contributors">Contributors</a></li>
|
||||
<li><a href="#closed_issues">Closed Issues</a></li>
|
||||
<li><a href="#diffstat">Diffstat</a></li>
|
||||
</ol><hr><a name="summary"><h2 align="center">Summary</h2></a><center><a href="#top">[Back to Top]</a></center><p>This release is a point release of an existing major version. The changes included were made to address problems that have been identified in this release series, or are minor, backwards compatible new features or improvements. Users should be able to safely upgrade to this version if this release series is already in use. Users considering upgrading from a previous version are strongly encouraged to review the UPGRADE.txt document as well as the CHANGES document for information about upgrading to this release series.</p><p>The data in this summary reflects changes that have been made since the previous release, asterisk-19.3.2.</p><hr><a name="contributors"><h2 align="center">Contributors</h2></a><center><a href="#top">[Back to Top]</a></center><p>This table lists the people who have submitted code, those that have tested patches, as well as those that reported issues on the issue tracker that were resolved in this release. For coders, the number is how many of their patches (of any size) were committed into this release. For testers, the number is the number of times their name was listed as assisting with testing a patch. Finally, for reporters, the number is the number of issues that they reported that were affected by commits that went into this release.</p><table width="100%" border="0">
|
||||
<tr><th width="33%">Coders</th><th width="33%">Testers</th><th width="33%">Reporters</th></tr>
|
||||
<tr valign="top"><td width="33%">1 Ben Ford <bford@digium.com><br/></td><td width="33%"><td width="33%">1 Claude Diderich <claude.diderich@yahoo.com><br/></td></tr>
|
||||
</table><hr><a name="closed_issues"><h2 align="center">Closed Issues</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a list of all issues from the issue tracker that were closed by changes that went into this release.</p><h3>Bug</h3><h4>Category: Resources/res_stir_shaken</h4><a href="https://issues.asterisk.org/jira/browse/ASTERISK-30024">ASTERISK-30024</a>: Failed to sign STIR/SHAKEN payload with functionality not enabled<br/>Reported by: Claude Diderich<ul>
|
||||
<li><a href="https://code.asterisk.org/code/changelog/asterisk?cs=0adbc4f499248aed746e2074504fd535c791e168">[0adbc4f499]</a> Ben Ford -- res_pjsip_stir_shaken.c: Fix enabled when not configured.</li>
|
||||
</ul><br><hr><a name="diffstat"><h2 align="center">Diffstat Results</h2></a><center><a href="#top">[Back to Top]</a></center><p>This is a summary of the changes to the source code that went into this release that was generated using the diffstat utility.</p><pre>0 files changed</pre><br></html>
|
82
asterisk-19.3.3-summary.txt
Normal file
82
asterisk-19.3.3-summary.txt
Normal file
@@ -0,0 +1,82 @@
|
||||
Release Summary
|
||||
|
||||
asterisk-19.3.3
|
||||
|
||||
Date: 2022-04-26
|
||||
|
||||
<asteriskteam@digium.com>
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Summary
|
||||
2. Contributors
|
||||
3. Closed Issues
|
||||
4. Diffstat
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Summary
|
||||
|
||||
[Back to Top]
|
||||
|
||||
This release is a point release of an existing major version. The changes
|
||||
included were made to address problems that have been identified in this
|
||||
release series, or are minor, backwards compatible new features or
|
||||
improvements. Users should be able to safely upgrade to this version if
|
||||
this release series is already in use. Users considering upgrading from a
|
||||
previous version are strongly encouraged to review the UPGRADE.txt
|
||||
document as well as the CHANGES document for information about upgrading
|
||||
to this release series.
|
||||
|
||||
The data in this summary reflects changes that have been made since the
|
||||
previous release, asterisk-19.3.2.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Contributors
|
||||
|
||||
[Back to Top]
|
||||
|
||||
This table lists the people who have submitted code, those that have
|
||||
tested patches, as well as those that reported issues on the issue tracker
|
||||
that were resolved in this release. For coders, the number is how many of
|
||||
their patches (of any size) were committed into this release. For testers,
|
||||
the number is the number of times their name was listed as assisting with
|
||||
testing a patch. Finally, for reporters, the number is the number of
|
||||
issues that they reported that were affected by commits that went into
|
||||
this release.
|
||||
|
||||
Coders Testers Reporters
|
||||
1 Ben Ford 1 Claude Diderich
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Closed Issues
|
||||
|
||||
[Back to Top]
|
||||
|
||||
This is a list of all issues from the issue tracker that were closed by
|
||||
changes that went into this release.
|
||||
|
||||
Bug
|
||||
|
||||
Category: Resources/res_stir_shaken
|
||||
|
||||
ASTERISK-30024: Failed to sign STIR/SHAKEN payload with functionality not
|
||||
enabled
|
||||
Reported by: Claude Diderich
|
||||
* [0adbc4f499] Ben Ford -- res_pjsip_stir_shaken.c: Fix enabled when not
|
||||
configured.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Diffstat Results
|
||||
|
||||
[Back to Top]
|
||||
|
||||
This is a summary of the changes to the source code that went into this
|
||||
release that was generated using the diffstat utility.
|
||||
|
||||
0 files changed
|
@@ -1,54 +0,0 @@
|
||||
#!/bin/sh
|
||||
PROGNAME="${0##*/}"
|
||||
|
||||
if [ "$1" = "-h" ] || [ "$1" = "--help" ] ; then
|
||||
cat <<-EOF
|
||||
Usage: ${PROGNAME}: [ <input_file> ] [ <output_file> ]
|
||||
|
||||
This script takes an Asterisk makeopts file, or any file containing
|
||||
"make" style variable assignments, and converts it into a format
|
||||
that can be directly 'sourced' by shell scripts.
|
||||
|
||||
* Any spaces around the equals sign are removed.
|
||||
* The variable value is quoted.
|
||||
* The "make" "or" command is evaluated.
|
||||
|
||||
Both input and output files are optional and will default to
|
||||
stdin and stdout respectively.
|
||||
|
||||
NOTE: This script relies on NO external commands and only POSIX
|
||||
constructs. It should be runnable by any shell.
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
input_file="/dev/stdin"
|
||||
if [ "$1" != "" ] ; then
|
||||
input_file="$1"
|
||||
fi
|
||||
|
||||
output_file="/dev/stdout"
|
||||
if [ "$2" != "" ] ; then
|
||||
output_file="$2"
|
||||
fi
|
||||
|
||||
# orfunc is a code fragment to be added to the outp[ut file.
|
||||
# We don't WANT the variables evaluated.
|
||||
# shellcheck disable=SC2016
|
||||
orfunc='or (){ before="${1%,*}" ; after="${1#*,}" ; if [ "$before" = "" ] ; then echo "${after}" ; else echo "${before}" ; fi ; }'
|
||||
echo "${orfunc}" >"${output_file}"
|
||||
|
||||
while read -r LINE ; do
|
||||
var="${LINE%%=*}"
|
||||
if [ "${var}" != "" ] ; then
|
||||
val="${LINE#*=}"
|
||||
if [ "${val}" != "${var}" ] ; then
|
||||
if [ "${val%% *}" = "" ] ; then
|
||||
echo "${var% *}=\"${val#* }\""
|
||||
else
|
||||
echo "${var% *}=\"${val}\""
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done <"${input_file}" >>"${output_file}"
|
||||
|
@@ -1,10 +1,7 @@
|
||||
#!/bin/sh
|
||||
# The GREP, SED, FIND, etc variables are all set at run time from
|
||||
# makeopts.
|
||||
# shellcheck disable=SC2154
|
||||
|
||||
PROGNAME="${0##*/}"
|
||||
PROGDIR="${0%/*}"
|
||||
|
||||
# Fail on errors
|
||||
set -e
|
||||
@@ -111,36 +108,25 @@ if [ ! -d "${source_tree}" ] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${source_tree}/Makefile" ] ; then
|
||||
echo "There's no 'Makefile' in '${source_tree}'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "${source_tree}/makeopts" ] ; then
|
||||
echo "There's no 'makeopts' in '${source_tree}'. Maybe you need to run ./configure?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# This will get the paths to the utilities we need, all
|
||||
# of which will be in makeopts. We need to convert the
|
||||
# format so it's sourceable.
|
||||
tmpname="/tmp/ast_makeopts.$$.env"
|
||||
trap 'rm "$tmpname" >/dev/null 2>&1' INT QUIT TERM EXIT
|
||||
"${PROGDIR}/get_sourceable_makeopts" "${source_tree}/makeopts" >"${tmpname}"
|
||||
# The file to be sourced is generated at run time and can't be checked.
|
||||
# shellcheck disable=SC1090
|
||||
. "${tmpname}"
|
||||
rm "${tmpname}" > /dev/null 2>&1 || :
|
||||
trap - INT QUIT TERM EXIT
|
||||
|
||||
# Make sure we have everything we need.
|
||||
for c in GREP FIND AWK DIRNAME BASENAME SED CAT ; do
|
||||
bin=$(eval "echo \${${c}}")
|
||||
if [ "${bin}" = "" ] ; then
|
||||
echo "The '${c}' utility was not found."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
# This script is normally run from the top-level Makefile which
|
||||
# will set the tools variables to actual paths, or ':' if
|
||||
# the tool isn't found. If this script is run from the
|
||||
# command line for testing purposes however, we'll need to
|
||||
# set some sane defaults.
|
||||
if [ "${GREP}" = "" ] ; then GREP="grep" ; fi
|
||||
if [ "${FIND}" = "" ] ; then FIND="find" ; fi
|
||||
if [ "${AWK}" = "" ] ; then AWK="awk" ; fi
|
||||
if [ "${DIRNAME}" = "" ] ; then DIRNAME="dirname" ; fi
|
||||
if [ "${BASENAME}" = "" ] ; then BASENAME="basename" ; fi
|
||||
if [ "${SED}" = "" ] ; then SED="sed" ; fi
|
||||
if [ "${CAT}" = "" ] ; then CAT="cat" ; fi
|
||||
if [ "${XMLLINT}" = "" ] ; then XMLLINT="xmllint" ; fi
|
||||
if [ "${XMLSTARLET}" = "" ] ; then XMLSTARLET="xmlstarlet" ; fi
|
||||
|
||||
if [ "${for_wiki}" -eq "1" ] || [ "${validate}" -eq "1" ]; then
|
||||
if [ "${XMLLINT}${XMLSTARLET}" = "::" ] ; then
|
||||
|
@@ -36,6 +36,10 @@
|
||||
; to use the dialplan function SQL_ESC() to escape the data prior to its
|
||||
; inclusion in the SQL statement.
|
||||
;
|
||||
; If you have data which may potentially contain backslashes, you may wish to
|
||||
; use the dialplan function SQL_ESC_BACKSLASHES() to escape the backslashes.
|
||||
; Note that not all databases may require escaping of the backslashes.
|
||||
;
|
||||
;
|
||||
; The following options are available in this configuration file:
|
||||
;
|
||||
|
@@ -349,6 +349,7 @@
|
||||
; STIR/SHAKEN support.
|
||||
;
|
||||
;stir_shaken=no
|
||||
;stir_shaken_profile=my_profile
|
||||
|
||||
;[6001]
|
||||
;type=auth
|
||||
@@ -930,6 +931,9 @@
|
||||
; happens to the call if verification fails; it's up to
|
||||
; you to determine what to do with the results.
|
||||
; (default: no)
|
||||
;stir_shaken_profile =
|
||||
; If a profile is specified (defined in stir_shaken.conf),
|
||||
; this endpoint will follow the rules defined there.
|
||||
;allow_unauthenticated_options =
|
||||
; By default, chan_pjsip will challenge an incoming
|
||||
; OPTIONS request for authentication credentials just
|
||||
|
@@ -83,3 +83,21 @@
|
||||
;
|
||||
; Must have an attestation of A, B, or C
|
||||
;attestation=C
|
||||
;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;
|
||||
; Profiles can be defined here which can be referenced by channel drivers.
|
||||
;[my_profile]
|
||||
;
|
||||
; type must be "profile"
|
||||
;type=profile
|
||||
;
|
||||
; Set stir_shaken to 'attest', 'verify', or 'on', which is the default
|
||||
;stir_shaken=on
|
||||
;
|
||||
; You can specify an ACL that will be used strictly for the Identity header when downloading public certificates
|
||||
;acllist=myacllist
|
||||
;
|
||||
; You can also do permit / deny lines if you want (also supports IPv6)
|
||||
;permit=0.0.0.0/0.0.0.0
|
||||
;deny=127.0.0.1
|
||||
|
41
contrib/realtime/mysql/mysql_cdr.sql
Normal file
41
contrib/realtime/mysql/mysql_cdr.sql
Normal file
@@ -0,0 +1,41 @@
|
||||
CREATE TABLE alembic_version (
|
||||
version_num VARCHAR(32) NOT NULL,
|
||||
CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
|
||||
);
|
||||
|
||||
-- Running upgrade -> 210693f3123d
|
||||
|
||||
CREATE TABLE cdr (
|
||||
accountcode VARCHAR(20),
|
||||
src VARCHAR(80),
|
||||
dst VARCHAR(80),
|
||||
dcontext VARCHAR(80),
|
||||
clid VARCHAR(80),
|
||||
channel VARCHAR(80),
|
||||
dstchannel VARCHAR(80),
|
||||
lastapp VARCHAR(80),
|
||||
lastdata VARCHAR(80),
|
||||
start DATETIME,
|
||||
answer DATETIME,
|
||||
end DATETIME,
|
||||
duration INTEGER,
|
||||
billsec INTEGER,
|
||||
disposition VARCHAR(45),
|
||||
amaflags VARCHAR(45),
|
||||
userfield VARCHAR(256),
|
||||
uniqueid VARCHAR(150),
|
||||
linkedid VARCHAR(150),
|
||||
peeraccount VARCHAR(20),
|
||||
sequence INTEGER
|
||||
);
|
||||
|
||||
INSERT INTO alembic_version (version_num) VALUES ('210693f3123d');
|
||||
|
||||
-- Running upgrade 210693f3123d -> 54cde9847798
|
||||
|
||||
ALTER TABLE cdr MODIFY accountcode VARCHAR(80) NULL;
|
||||
|
||||
ALTER TABLE cdr MODIFY peeraccount VARCHAR(80) NULL;
|
||||
|
||||
UPDATE alembic_version SET version_num='54cde9847798' WHERE alembic_version.version_num = '210693f3123d';
|
||||
|
1336
contrib/realtime/mysql/mysql_config.sql
Normal file
1336
contrib/realtime/mysql/mysql_config.sql
Normal file
File diff suppressed because it is too large
Load Diff
35
contrib/realtime/mysql/mysql_voicemail.sql
Normal file
35
contrib/realtime/mysql/mysql_voicemail.sql
Normal file
@@ -0,0 +1,35 @@
|
||||
CREATE TABLE alembic_version (
|
||||
version_num VARCHAR(32) NOT NULL,
|
||||
CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
|
||||
);
|
||||
|
||||
-- Running upgrade -> a2e9769475e
|
||||
|
||||
CREATE TABLE voicemail_messages (
|
||||
dir VARCHAR(255) NOT NULL,
|
||||
msgnum INTEGER NOT NULL,
|
||||
context VARCHAR(80),
|
||||
macrocontext VARCHAR(80),
|
||||
callerid VARCHAR(80),
|
||||
origtime INTEGER,
|
||||
duration INTEGER,
|
||||
recording BLOB,
|
||||
flag VARCHAR(30),
|
||||
category VARCHAR(30),
|
||||
mailboxuser VARCHAR(30),
|
||||
mailboxcontext VARCHAR(30),
|
||||
msg_id VARCHAR(40)
|
||||
);
|
||||
|
||||
ALTER TABLE voicemail_messages ADD CONSTRAINT voicemail_messages_dir_msgnum PRIMARY KEY (dir, msgnum);
|
||||
|
||||
CREATE INDEX voicemail_messages_dir ON voicemail_messages (dir);
|
||||
|
||||
INSERT INTO alembic_version (version_num) VALUES ('a2e9769475e');
|
||||
|
||||
-- Running upgrade a2e9769475e -> 39428242f7f5
|
||||
|
||||
ALTER TABLE voicemail_messages MODIFY recording BLOB(4294967295) NULL;
|
||||
|
||||
UPDATE alembic_version SET version_num='39428242f7f5' WHERE alembic_version.version_num = 'a2e9769475e';
|
||||
|
45
contrib/realtime/postgresql/postgresql_cdr.sql
Normal file
45
contrib/realtime/postgresql/postgresql_cdr.sql
Normal file
@@ -0,0 +1,45 @@
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE alembic_version (
|
||||
version_num VARCHAR(32) NOT NULL,
|
||||
CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
|
||||
);
|
||||
|
||||
-- Running upgrade -> 210693f3123d
|
||||
|
||||
CREATE TABLE cdr (
|
||||
accountcode VARCHAR(20),
|
||||
src VARCHAR(80),
|
||||
dst VARCHAR(80),
|
||||
dcontext VARCHAR(80),
|
||||
clid VARCHAR(80),
|
||||
channel VARCHAR(80),
|
||||
dstchannel VARCHAR(80),
|
||||
lastapp VARCHAR(80),
|
||||
lastdata VARCHAR(80),
|
||||
start TIMESTAMP WITHOUT TIME ZONE,
|
||||
answer TIMESTAMP WITHOUT TIME ZONE,
|
||||
"end" TIMESTAMP WITHOUT TIME ZONE,
|
||||
duration INTEGER,
|
||||
billsec INTEGER,
|
||||
disposition VARCHAR(45),
|
||||
amaflags VARCHAR(45),
|
||||
userfield VARCHAR(256),
|
||||
uniqueid VARCHAR(150),
|
||||
linkedid VARCHAR(150),
|
||||
peeraccount VARCHAR(20),
|
||||
sequence INTEGER
|
||||
);
|
||||
|
||||
INSERT INTO alembic_version (version_num) VALUES ('210693f3123d');
|
||||
|
||||
-- Running upgrade 210693f3123d -> 54cde9847798
|
||||
|
||||
ALTER TABLE cdr ALTER COLUMN accountcode TYPE VARCHAR(80);
|
||||
|
||||
ALTER TABLE cdr ALTER COLUMN peeraccount TYPE VARCHAR(80);
|
||||
|
||||
UPDATE alembic_version SET version_num='54cde9847798' WHERE alembic_version.version_num = '210693f3123d';
|
||||
|
||||
COMMIT;
|
||||
|
1448
contrib/realtime/postgresql/postgresql_config.sql
Normal file
1448
contrib/realtime/postgresql/postgresql_config.sql
Normal file
File diff suppressed because it is too large
Load Diff
39
contrib/realtime/postgresql/postgresql_voicemail.sql
Normal file
39
contrib/realtime/postgresql/postgresql_voicemail.sql
Normal file
@@ -0,0 +1,39 @@
|
||||
BEGIN;
|
||||
|
||||
CREATE TABLE alembic_version (
|
||||
version_num VARCHAR(32) NOT NULL,
|
||||
CONSTRAINT alembic_version_pkc PRIMARY KEY (version_num)
|
||||
);
|
||||
|
||||
-- Running upgrade -> a2e9769475e
|
||||
|
||||
CREATE TABLE voicemail_messages (
|
||||
dir VARCHAR(255) NOT NULL,
|
||||
msgnum INTEGER NOT NULL,
|
||||
context VARCHAR(80),
|
||||
macrocontext VARCHAR(80),
|
||||
callerid VARCHAR(80),
|
||||
origtime INTEGER,
|
||||
duration INTEGER,
|
||||
recording BYTEA,
|
||||
flag VARCHAR(30),
|
||||
category VARCHAR(30),
|
||||
mailboxuser VARCHAR(30),
|
||||
mailboxcontext VARCHAR(30),
|
||||
msg_id VARCHAR(40)
|
||||
);
|
||||
|
||||
ALTER TABLE voicemail_messages ADD CONSTRAINT voicemail_messages_dir_msgnum PRIMARY KEY (dir, msgnum);
|
||||
|
||||
CREATE INDEX voicemail_messages_dir ON voicemail_messages (dir);
|
||||
|
||||
INSERT INTO alembic_version (version_num) VALUES ('a2e9769475e');
|
||||
|
||||
-- Running upgrade a2e9769475e -> 39428242f7f5
|
||||
|
||||
ALTER TABLE voicemail_messages ALTER COLUMN recording TYPE BYTEA;
|
||||
|
||||
UPDATE alembic_version SET version_num='39428242f7f5' WHERE alembic_version.version_num = 'a2e9769475e';
|
||||
|
||||
COMMIT;
|
||||
|
@@ -96,6 +96,19 @@
|
||||
<para>Example: SELECT foo FROM bar WHERE baz='${SQL_ESC(${ARG1})}'</para>
|
||||
</description>
|
||||
</function>
|
||||
<function name="SQL_ESC_BACKSLASHES" language="en_US">
|
||||
<synopsis>
|
||||
Escapes backslashes for use in SQL statements.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="string" required="true" />
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Used in SQL templates to escape data which may contain backslashes
|
||||
<literal>\</literal> which are otherwise used to escape data.</para>
|
||||
<para>Example: SELECT foo FROM bar WHERE baz='${SQL_ESC(${SQL_ESC_BACKSLASHES(${ARG1})})}'</para>
|
||||
</description>
|
||||
</function>
|
||||
***/
|
||||
|
||||
static char *config = "func_odbc.conf";
|
||||
@@ -1102,13 +1115,13 @@ end_acf_read:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len, char character)
|
||||
{
|
||||
char *out = buf;
|
||||
|
||||
for (; *data && out - buf < len; data++) {
|
||||
if (*data == '\'') {
|
||||
*out = '\'';
|
||||
if (*data == character) {
|
||||
*out = character;
|
||||
out++;
|
||||
}
|
||||
*out++ = *data;
|
||||
@@ -1118,9 +1131,25 @@ static int acf_escape(struct ast_channel *chan, const char *cmd, char *data, cha
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acf_escape_ticks(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
return acf_escape(chan, cmd, data, buf, len, '\'');
|
||||
}
|
||||
|
||||
static struct ast_custom_function escape_function = {
|
||||
.name = "SQL_ESC",
|
||||
.read = acf_escape,
|
||||
.read = acf_escape_ticks,
|
||||
.write = NULL,
|
||||
};
|
||||
|
||||
static int acf_escape_backslashes(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
|
||||
{
|
||||
return acf_escape(chan, cmd, data, buf, len, '\\');
|
||||
}
|
||||
|
||||
static struct ast_custom_function escape_backslashes_function = {
|
||||
.name = "SQL_ESC_BACKSLASHES",
|
||||
.read = acf_escape_backslashes,
|
||||
.write = NULL,
|
||||
};
|
||||
|
||||
@@ -1858,6 +1887,7 @@ static int load_module(void)
|
||||
|
||||
ast_config_destroy(cfg);
|
||||
res |= ast_custom_function_register(&escape_function);
|
||||
res |= ast_custom_function_register(&escape_backslashes_function);
|
||||
ast_cli_register_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
|
||||
|
||||
AST_RWLIST_UNLOCK(&queries);
|
||||
@@ -1877,6 +1907,7 @@ static int unload_module(void)
|
||||
}
|
||||
|
||||
res |= ast_custom_function_unregister(&escape_function);
|
||||
res |= ast_custom_function_unregister(&escape_backslashes_function);
|
||||
res |= ast_custom_function_unregister(&fetch_function);
|
||||
res |= ast_unregister_application(app_odbcfinish);
|
||||
ast_cli_unregister_multiple(cli_func_odbc, ARRAY_LEN(cli_func_odbc));
|
||||
|
@@ -878,6 +878,8 @@ struct ast_sip_endpoint {
|
||||
AST_STRING_FIELD(accountcode);
|
||||
/*! If set, we'll push incoming MWI NOTIFYs to stasis using this mailbox */
|
||||
AST_STRING_FIELD(incoming_mwi_mailbox);
|
||||
/*! STIR/SHAKEN profile to use */
|
||||
AST_STRING_FIELD(stir_shaken_profile);
|
||||
);
|
||||
/*! Configuration for extensions */
|
||||
struct ast_sip_endpoint_extensions extensions;
|
||||
|
@@ -38,6 +38,8 @@ enum ast_stir_shaken_verify_failure_reason {
|
||||
|
||||
struct ast_stir_shaken_payload;
|
||||
|
||||
struct ast_acl_list;
|
||||
|
||||
struct ast_json;
|
||||
|
||||
/*!
|
||||
@@ -65,6 +67,38 @@ char *ast_stir_shaken_payload_get_public_cert_url(const struct ast_stir_shaken_p
|
||||
*/
|
||||
unsigned int ast_stir_shaken_get_signature_timeout(void);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve a stir_shaken_profile by id
|
||||
*
|
||||
* \note The profile will need to be unref'd when not needed anymore
|
||||
*
|
||||
* \param id The id of the stir_shaken_profile to get
|
||||
*
|
||||
* \retval stir_shaken_profile on success
|
||||
* \retval NULL on failure
|
||||
*/
|
||||
struct stir_shaken_profile *ast_stir_shaken_get_profile(const char *id);
|
||||
|
||||
/*!
|
||||
* \brief Check if a stir_shaken_profile supports attestation
|
||||
*
|
||||
* \param profile The stir_shaken_profile to test
|
||||
*
|
||||
* \retval 0 if not supported
|
||||
* \retval 1 if supported
|
||||
*/
|
||||
unsigned int ast_stir_shaken_profile_supports_attestation(const struct stir_shaken_profile *profile);
|
||||
|
||||
/*!
|
||||
* \brief Check if a stir_shaken_profile supports verification
|
||||
*
|
||||
* \param profile The stir_shaken_profile to test
|
||||
*
|
||||
* \retval 0 if not supported
|
||||
* \retval 1 if supported
|
||||
*/
|
||||
unsigned int ast_stir_shaken_profile_supports_verification(const struct stir_shaken_profile *profile);
|
||||
|
||||
/*!
|
||||
* \brief Add a STIR/SHAKEN verification result to a channel
|
||||
*
|
||||
@@ -112,6 +146,26 @@ struct ast_stir_shaken_payload *ast_stir_shaken_verify(const char *header, const
|
||||
struct ast_stir_shaken_payload *ast_stir_shaken_verify2(const char *header, const char *payload, const char *signature,
|
||||
const char *algorithm, const char *public_cert_url, int *failure_code);
|
||||
|
||||
/*!
|
||||
* \brief Same as ast_stir_shaken_verify2, but passes in a stir_shaken_profile with additional configuration
|
||||
*
|
||||
* \note failure_code will be written to in this function
|
||||
*
|
||||
* \param header The payload header
|
||||
* \param payload The payload section
|
||||
* \param signature The payload signature
|
||||
* \param algorithm The signature algorithm
|
||||
* \param public_cert_url The public key URL
|
||||
* \param failure_code Additional failure information
|
||||
* \param profile The stir_shaken_profile
|
||||
*
|
||||
* \retval ast_stir_shaken_payload on success
|
||||
* \retval NULL on failure
|
||||
*/
|
||||
struct ast_stir_shaken_payload *ast_stir_shaken_verify_with_profile(const char *header, const char *payload,
|
||||
const char *signature, const char *algorithm, const char *public_cert_url, int *failure,
|
||||
const struct stir_shaken_profile *profile);
|
||||
|
||||
/*!
|
||||
* \brief Retrieve the stir/shaken sorcery context
|
||||
*
|
||||
|
@@ -1424,6 +1424,13 @@
|
||||
INVITEs, an Identity header will be added.</para>
|
||||
</description>
|
||||
</configOption>
|
||||
<configOption name="stir_shaken_profile" default="">
|
||||
<synopsis>STIR/SHAKEN profile containing additional configuration options</synopsis>
|
||||
<description><para>
|
||||
A STIR/SHAKEN profile that is defined in stir_shaken.conf. Contains
|
||||
several options and rules used for STIR/SHAKEN.</para>
|
||||
</description>
|
||||
</configOption>
|
||||
<configOption name="allow_unauthenticated_options" default="no">
|
||||
<synopsis>Skip authentication when receiving OPTIONS requests</synopsis>
|
||||
<description><para>
|
||||
|
@@ -2192,6 +2192,7 @@ int ast_res_pjsip_initialize_configuration(void)
|
||||
"prefer: pending, operation: intersect, keep: all",
|
||||
codec_prefs_handler, outgoing_answer_codec_prefs_to_str, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "stir_shaken", "off", stir_shaken_handler, stir_shaken_to_str, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "stir_shaken_profile", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, stir_shaken_profile));
|
||||
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow_unauthenticated_options", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, allow_unauthenticated_options));
|
||||
|
||||
if (ast_sip_initialize_sorcery_transport()) {
|
||||
|
@@ -217,13 +217,21 @@ static int stir_shaken_incoming_request(struct ast_sip_session *session, pjsip_r
|
||||
int mismatch = 0;
|
||||
struct ast_stir_shaken_payload *ss_payload;
|
||||
int failure_code = 0;
|
||||
RAII_VAR(struct stir_shaken_profile *, profile, NULL, ao2_cleanup);
|
||||
|
||||
/* Check if this is a reinvite. If it is, we don't need to do anything */
|
||||
if (rdata->msg_info.to->tag.slen) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((session->endpoint->stir_shaken & AST_SIP_STIR_SHAKEN_VERIFY) == 0) {
|
||||
profile = ast_stir_shaken_get_profile(session->endpoint->stir_shaken_profile);
|
||||
/* Profile should be checked first as it takes priority over anything else.
|
||||
* If there is a profile and it doesn't have verification enabled, do nothing.
|
||||
* If there is no profile and the stir_shaken option is either not set or does
|
||||
* not support verification, do nothing.
|
||||
*/
|
||||
if ((profile && !ast_stir_shaken_profile_supports_verification(profile))
|
||||
|| (!profile && (session->endpoint->stir_shaken & AST_SIP_STIR_SHAKEN_VERIFY) == 0)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -309,7 +317,8 @@ static int stir_shaken_incoming_request(struct ast_sip_session *session, pjsip_r
|
||||
|
||||
attestation = get_attestation_from_payload(payload);
|
||||
|
||||
ss_payload = ast_stir_shaken_verify2(header, payload, signature, algorithm, public_cert_url, &failure_code);
|
||||
ss_payload = ast_stir_shaken_verify_with_profile(header, payload, signature, algorithm, public_cert_url, &failure_code, profile);
|
||||
|
||||
if (!ss_payload) {
|
||||
|
||||
if (failure_code == AST_STIR_SHAKEN_VERIFY_FAILED_TO_GET_CERT) {
|
||||
@@ -471,7 +480,16 @@ static void add_date_header(const struct ast_sip_session *session, pjsip_tx_data
|
||||
|
||||
static void stir_shaken_outgoing_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
|
||||
{
|
||||
if ((session->endpoint->stir_shaken & AST_SIP_STIR_SHAKEN_ATTEST) == 0) {
|
||||
RAII_VAR(struct stir_shaken_profile *, profile, NULL, ao2_cleanup);
|
||||
|
||||
profile = ast_stir_shaken_get_profile(session->endpoint->stir_shaken_profile);
|
||||
/* Profile should be checked first as it takes priority over anything else.
|
||||
* If there is a profile and it doesn't have attestation enabled, do nothing.
|
||||
* If there is no profile and the stir_shaken option is either not set or does
|
||||
* not support attestation, do nothing.
|
||||
*/
|
||||
if ((profile && !ast_stir_shaken_profile_supports_attestation(profile))
|
||||
|| (!profile && (session->endpoint->stir_shaken & AST_SIP_STIR_SHAKEN_ATTEST) == 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -38,6 +38,7 @@
|
||||
#include "asterisk/global_datastores.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/test.h"
|
||||
#include "asterisk/acl.h"
|
||||
|
||||
#include "asterisk/res_stir_shaken.h"
|
||||
#include "res_stir_shaken/stir_shaken.h"
|
||||
@@ -45,6 +46,7 @@
|
||||
#include "res_stir_shaken/store.h"
|
||||
#include "res_stir_shaken/certificate.h"
|
||||
#include "res_stir_shaken/curl.h"
|
||||
#include "res_stir_shaken/profile.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<configInfo name="res_stir_shaken" language="en_US">
|
||||
@@ -108,6 +110,29 @@
|
||||
<synopsis>The caller ID number to match on.</synopsis>
|
||||
</configOption>
|
||||
</configObject>
|
||||
<configObject name="profile">
|
||||
<synopsis>STIR/SHAKEN profile configuration options</synopsis>
|
||||
<configOption name="type">
|
||||
<synopsis>Must be of type 'profile'.</synopsis>
|
||||
</configOption>
|
||||
<configOption name="stir_shaken" default="on">
|
||||
<synopsis>STIR/SHAKEN configuration settings</synopsis>
|
||||
<description><para>
|
||||
Attest, verify, or do both STIR/SHAKEN operations. On incoming
|
||||
INVITEs, the Identity header will be checked for validity. On
|
||||
outgoing INVITEs, an Identity header will be added.</para>
|
||||
</description>
|
||||
</configOption>
|
||||
<configOption name="acllist" default="">
|
||||
<synopsis>An existing ACL from acl.conf to use</synopsis>
|
||||
</configOption>
|
||||
<configOption name="permit" default="">
|
||||
<synopsis>An IP or subnet to permit</synopsis>
|
||||
</configOption>
|
||||
<configOption name="deny" default="">
|
||||
<synopsis>An IP or subnet to deny</synopsis>
|
||||
</configOption>
|
||||
</configObject>
|
||||
</configFile>
|
||||
</configInfo>
|
||||
<function name="STIR_SHAKEN" language="en_US">
|
||||
@@ -205,6 +230,33 @@ unsigned int ast_stir_shaken_get_signature_timeout(void)
|
||||
return ast_stir_shaken_signature_timeout(stir_shaken_general_get());
|
||||
}
|
||||
|
||||
struct stir_shaken_profile *ast_stir_shaken_get_profile(const char *id)
|
||||
{
|
||||
if (ast_strlen_zero(id)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ast_stir_shaken_get_profile_by_name(id);
|
||||
}
|
||||
|
||||
unsigned int ast_stir_shaken_profile_supports_attestation(const struct stir_shaken_profile *profile)
|
||||
{
|
||||
if (!profile) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (profile->stir_shaken & STIR_SHAKEN_ATTEST);
|
||||
}
|
||||
|
||||
unsigned int ast_stir_shaken_profile_supports_verification(const struct stir_shaken_profile *profile)
|
||||
{
|
||||
if (!profile) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (profile->stir_shaken & STIR_SHAKEN_VERIFY);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Convert an ast_stir_shaken_verification_result to string representation
|
||||
*
|
||||
@@ -554,7 +606,7 @@ static int stir_shaken_verify_signature(const char *msg, const char *signature,
|
||||
* \retval NULL on failure
|
||||
* \retval full path filename on success
|
||||
*/
|
||||
static char *run_curl(const char *public_cert_url, const char *path)
|
||||
static char *run_curl(const char *public_cert_url, const char *path, const struct ast_acl_list *acl)
|
||||
{
|
||||
struct curl_cb_data *data;
|
||||
char *filename;
|
||||
@@ -565,7 +617,7 @@ static char *run_curl(const char *public_cert_url, const char *path)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
filename = curl_public_key(public_cert_url, path, data);
|
||||
filename = curl_public_key(public_cert_url, path, data, acl);
|
||||
if (!filename) {
|
||||
ast_log(LOG_ERROR, "Could not retrieve public key for '%s'\n", public_cert_url);
|
||||
curl_cb_data_free(data);
|
||||
@@ -591,7 +643,7 @@ static char *run_curl(const char *public_cert_url, const char *path)
|
||||
* \retval NULL on failure
|
||||
* \retval full path filename on success
|
||||
*/
|
||||
static char *curl_and_check_expiration(const char *public_cert_url, const char *path, int *curl)
|
||||
static char *curl_and_check_expiration(const char *public_cert_url, const char *path, int *curl, const struct ast_acl_list *acl)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
@@ -600,7 +652,7 @@ static char *curl_and_check_expiration(const char *public_cert_url, const char *
|
||||
return NULL;
|
||||
}
|
||||
|
||||
filename = run_curl(public_cert_url, path);
|
||||
filename = run_curl(public_cert_url, path, acl);
|
||||
if (!filename) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -662,7 +714,8 @@ static int stir_shaken_verify_check_empty_strings(const char *header, const char
|
||||
* \retval 0 on success
|
||||
* \retval 1 on failure
|
||||
*/
|
||||
static int stir_shaken_verify_setup_file_paths(const char *public_cert_url, char **file_path, char **dir_path, int *curl)
|
||||
static int stir_shaken_verify_setup_file_paths(const char *public_cert_url, char **file_path, char **dir_path, int *curl,
|
||||
const struct ast_acl_list *acl)
|
||||
{
|
||||
*file_path = get_path_to_public_key(public_cert_url);
|
||||
if (ast_asprintf(dir_path, "%s/keys/%s", ast_config_AST_DATA_DIR, STIR_SHAKEN_DIR_NAME) < 0) {
|
||||
@@ -680,7 +733,7 @@ static int stir_shaken_verify_setup_file_paths(const char *public_cert_url, char
|
||||
ast_free(*file_path);
|
||||
|
||||
/* Download to the default path */
|
||||
*file_path = run_curl(public_cert_url, *dir_path);
|
||||
*file_path = run_curl(public_cert_url, *dir_path, acl);
|
||||
if (!(*file_path)) {
|
||||
return 1;
|
||||
}
|
||||
@@ -704,7 +757,7 @@ static int stir_shaken_verify_setup_file_paths(const char *public_cert_url, char
|
||||
* \retval 1 on failure
|
||||
*/
|
||||
static int stir_shaken_verify_validate_cert(const char *public_cert_url, char **file_path, char *dir_path, int *curl,
|
||||
EVP_PKEY **public_key)
|
||||
EVP_PKEY **public_key, const struct ast_acl_list *acl)
|
||||
{
|
||||
if (public_key_is_expired(public_cert_url)) {
|
||||
|
||||
@@ -714,7 +767,7 @@ static int stir_shaken_verify_validate_cert(const char *public_cert_url, char **
|
||||
|
||||
/* If this fails, then there's nothing we can do */
|
||||
ast_free(*file_path);
|
||||
*file_path = curl_and_check_expiration(public_cert_url, dir_path, curl);
|
||||
*file_path = curl_and_check_expiration(public_cert_url, dir_path, curl, acl);
|
||||
if (!(*file_path)) {
|
||||
return 1;
|
||||
}
|
||||
@@ -730,7 +783,7 @@ static int stir_shaken_verify_validate_cert(const char *public_cert_url, char **
|
||||
remove_public_key_from_astdb(public_cert_url);
|
||||
|
||||
ast_free(*file_path);
|
||||
*file_path = curl_and_check_expiration(public_cert_url, dir_path, curl);
|
||||
*file_path = curl_and_check_expiration(public_cert_url, dir_path, curl, acl);
|
||||
if (!(*file_path)) {
|
||||
return 1;
|
||||
}
|
||||
@@ -756,6 +809,12 @@ struct ast_stir_shaken_payload *ast_stir_shaken_verify(const char *header, const
|
||||
|
||||
struct ast_stir_shaken_payload *ast_stir_shaken_verify2(const char *header, const char *payload, const char *signature,
|
||||
const char *algorithm, const char *public_cert_url, int *failure_code)
|
||||
{
|
||||
return ast_stir_shaken_verify_with_profile(header, payload, signature, algorithm, public_cert_url, failure_code, NULL);
|
||||
}
|
||||
|
||||
struct ast_stir_shaken_payload *ast_stir_shaken_verify_with_profile(const char *header, const char *payload, const char *signature,
|
||||
const char *algorithm, const char *public_cert_url, int *failure_code, const struct stir_shaken_profile *profile)
|
||||
{
|
||||
struct ast_stir_shaken_payload *ret_payload;
|
||||
EVP_PKEY *public_key;
|
||||
@@ -764,11 +823,14 @@ struct ast_stir_shaken_payload *ast_stir_shaken_verify2(const char *header, cons
|
||||
RAII_VAR(char *, dir_path, NULL, ast_free);
|
||||
RAII_VAR(char *, combined_str, NULL, ast_free);
|
||||
size_t combined_size;
|
||||
const struct ast_acl_list *acl;
|
||||
|
||||
if (stir_shaken_verify_check_empty_strings(header, payload, signature, algorithm, public_cert_url)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
acl = profile ? (const struct ast_acl_list *)profile->acl : NULL;
|
||||
|
||||
/* Check to see if we have already downloaded this public cert. The reason we
|
||||
* store the file path is because:
|
||||
*
|
||||
@@ -779,12 +841,12 @@ struct ast_stir_shaken_payload *ast_stir_shaken_verify2(const char *header, cons
|
||||
* {configurable) directories, we already have the storage mechanism in place.
|
||||
* The only thing that would be left to do is pull from the configuration.
|
||||
*/
|
||||
if (stir_shaken_verify_setup_file_paths(public_cert_url, &file_path, &dir_path, &curl)) {
|
||||
if (stir_shaken_verify_setup_file_paths(public_cert_url, &file_path, &dir_path, &curl, acl)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check to see if the cert we downloaded (or already had) is expired */
|
||||
if (stir_shaken_verify_validate_cert(public_cert_url, &file_path, dir_path, &curl, &public_key)) {
|
||||
if (stir_shaken_verify_validate_cert(public_cert_url, &file_path, dir_path, &curl, &public_key, acl)) {
|
||||
*failure_code = AST_STIR_SHAKEN_VERIFY_FAILED_TO_GET_CERT;
|
||||
return NULL;
|
||||
}
|
||||
@@ -1677,6 +1739,7 @@ static int unload_module(void)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
stir_shaken_profile_unload();
|
||||
stir_shaken_certificate_unload();
|
||||
stir_shaken_store_unload();
|
||||
stir_shaken_general_unload();
|
||||
@@ -1716,6 +1779,11 @@ static int load_module(void)
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
if (stir_shaken_profile_load()) {
|
||||
unload_module();
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
ast_sorcery_load(ast_stir_shaken_sorcery());
|
||||
|
||||
res |= ast_custom_function_register(&stir_shaken_function);
|
||||
|
@@ -21,9 +21,12 @@
|
||||
#include "asterisk/utils.h"
|
||||
#include "asterisk/logger.h"
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/acl.h"
|
||||
|
||||
#include "curl.h"
|
||||
#include "general.h"
|
||||
#include "stir_shaken.h"
|
||||
#include "profile.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -31,12 +34,32 @@
|
||||
/* Used to check CURL headers */
|
||||
#define MAX_HEADER_LENGTH 1023
|
||||
|
||||
/* Used to limit download size */
|
||||
#define MAX_DOWNLOAD_SIZE 8192
|
||||
|
||||
/* Used to limit how many bytes we get from CURL per write */
|
||||
#define MAX_BUF_SIZE_PER_WRITE 1024
|
||||
|
||||
/* Certificates should begin with this */
|
||||
#define BEGIN_CERTIFICATE_STR "-----BEGIN CERTIFICATE-----"
|
||||
|
||||
/* CURL callback data to avoid storing useless info in AstDB */
|
||||
struct curl_cb_data {
|
||||
char *cache_control;
|
||||
char *expires;
|
||||
};
|
||||
|
||||
struct curl_cb_write_buf {
|
||||
char buf[MAX_DOWNLOAD_SIZE + 1];
|
||||
size_t size;
|
||||
const char *url;
|
||||
};
|
||||
|
||||
struct curl_cb_open_socket {
|
||||
const struct ast_acl_list *acl;
|
||||
curl_socket_t *sockfd;
|
||||
};
|
||||
|
||||
struct curl_cb_data *curl_cb_data_create(void)
|
||||
{
|
||||
struct curl_cb_data *data;
|
||||
@@ -58,6 +81,18 @@ void curl_cb_data_free(struct curl_cb_data *data)
|
||||
ast_free(data);
|
||||
}
|
||||
|
||||
static void curl_cb_open_socket_free(struct curl_cb_open_socket *data)
|
||||
{
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
close(*data->sockfd);
|
||||
|
||||
/* We don't need to free the ACL since we just use a reference */
|
||||
ast_free(data);
|
||||
}
|
||||
|
||||
char *curl_cb_data_get_cache_control(const struct curl_cb_data *data)
|
||||
{
|
||||
if (!data) {
|
||||
@@ -149,94 +184,168 @@ static CURL *get_curl_instance(struct curl_cb_data *data)
|
||||
return curl;
|
||||
}
|
||||
|
||||
char *curl_public_key(const char *public_cert_url, const char *path, struct curl_cb_data *data)
|
||||
/*!
|
||||
* \brief Write callback passed to libcurl
|
||||
*
|
||||
* \note If this function returns anything other than the size of the data
|
||||
* libcurl expected us to process, the request will cancel. That's why we return
|
||||
* 0 on error, otherwise the amount of data we were given
|
||||
*
|
||||
* \param curl_data The data from libcurl
|
||||
* \param size Always 1 according to libcurl
|
||||
* \param actual_size The actual size of the data
|
||||
* \param our_data The data we passed to libcurl
|
||||
*
|
||||
* \retval The size of the data we processed
|
||||
* \retval 0 if there was an error
|
||||
*/
|
||||
static size_t curl_write_cb(void *curl_data, size_t size, size_t actual_size, void *our_data)
|
||||
{
|
||||
/* Just in case size is NOT always 1 or if it's changed in the future, let's go ahead
|
||||
* and do the math for the actual size */
|
||||
size_t real_size = size * actual_size;
|
||||
struct curl_cb_write_buf *buf = our_data;
|
||||
size_t new_size = buf->size + real_size;
|
||||
|
||||
if (new_size > MAX_DOWNLOAD_SIZE) {
|
||||
ast_log(LOG_WARNING, "Attempted to retrieve certificate from %s failed "
|
||||
"because it's size exceeds the maximum %d bytes\n", buf->url, MAX_DOWNLOAD_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(&(buf->buf[buf->size]), curl_data, real_size);
|
||||
buf->size += real_size;
|
||||
buf->buf[buf->size] = 0;
|
||||
|
||||
return real_size;
|
||||
}
|
||||
|
||||
static curl_socket_t stir_shaken_curl_open_socket_callback(void *our_data, curlsocktype purpose, struct curl_sockaddr *address)
|
||||
{
|
||||
struct curl_cb_open_socket *data = our_data;
|
||||
|
||||
if (!ast_acl_list_is_empty((struct ast_acl_list *)data->acl)) {
|
||||
struct ast_sockaddr ast_address = { {0,} };
|
||||
|
||||
ast_sockaddr_copy_sockaddr(&ast_address, &address->addr, address->addrlen);
|
||||
|
||||
if (ast_apply_acl((struct ast_acl_list *)data->acl, &ast_address, NULL) != AST_SENSE_ALLOW) {
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
}
|
||||
}
|
||||
|
||||
*data->sockfd = socket(address->family, address->socktype, address->protocol);
|
||||
|
||||
return *data->sockfd;
|
||||
}
|
||||
|
||||
char *curl_public_key(const char *public_cert_url, const char *path, struct curl_cb_data *data, const struct ast_acl_list *acl)
|
||||
{
|
||||
FILE *public_key_file;
|
||||
RAII_VAR(char *, tmp_filename, NULL, ast_free);
|
||||
const char *template_name = "certXXXXXX";
|
||||
char *filename;
|
||||
char *serial;
|
||||
int fd;
|
||||
long http_code;
|
||||
CURL *curl;
|
||||
char curl_errbuf[CURL_ERROR_SIZE + 1];
|
||||
struct curl_cb_write_buf *buf;
|
||||
struct curl_cb_open_socket *open_socket_data;
|
||||
curl_socket_t sockfd;
|
||||
|
||||
curl_errbuf[CURL_ERROR_SIZE] = '\0';
|
||||
|
||||
/* For now, it's fine to pass in path as is - it shouldn't end with a '/'. However,
|
||||
* if we decide to change how certificates are stored in the future (configurable paths),
|
||||
* then we will need to check to see if path ends with '/', copy everything up to the '/',
|
||||
* and use this new variable for ast_create_temp_file as well as for ast_asprintf below.
|
||||
*/
|
||||
fd = ast_file_fdtemp(path, &tmp_filename, template_name);
|
||||
if (fd == -1) {
|
||||
ast_log(LOG_ERROR, "Failed to get temporary file descriptor for CURL\n");
|
||||
buf = ast_calloc(1, sizeof(*buf));
|
||||
if (!buf) {
|
||||
ast_log(LOG_ERROR, "Failed to allocate memory for CURL write buffer for %s\n", public_cert_url);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public_key_file = fdopen(fd, "wb");
|
||||
if (!public_key_file) {
|
||||
ast_log(LOG_ERROR, "Failed to open file '%s' to write public key from '%s': %s (%d)\n",
|
||||
tmp_filename, public_cert_url, strerror(errno), errno);
|
||||
close(fd);
|
||||
remove(tmp_filename);
|
||||
open_socket_data = ast_calloc(1, sizeof(*open_socket_data));
|
||||
if (!open_socket_data) {
|
||||
ast_log(LOG_ERROR, "Failed to allocate memory for open socket callback\n");
|
||||
return NULL;
|
||||
}
|
||||
open_socket_data->acl = acl;
|
||||
open_socket_data->sockfd = &sockfd;
|
||||
|
||||
buf->url = public_cert_url;
|
||||
curl_errbuf[CURL_ERROR_SIZE] = '\0';
|
||||
|
||||
curl = get_curl_instance(data);
|
||||
if (!curl) {
|
||||
ast_log(LOG_ERROR, "Failed to set up CURL instance for '%s'\n", public_cert_url);
|
||||
fclose(public_key_file);
|
||||
remove(tmp_filename);
|
||||
ast_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_URL, public_cert_url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, public_key_file);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_cb);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, buf);
|
||||
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
|
||||
curl_easy_setopt(curl, CURLOPT_BUFFERSIZE, MAX_BUF_SIZE_PER_WRITE);
|
||||
curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, stir_shaken_curl_open_socket_callback);
|
||||
curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, open_socket_data);
|
||||
|
||||
if (curl_easy_perform(curl)) {
|
||||
ast_log(LOG_ERROR, "%s\n", curl_errbuf);
|
||||
curl_easy_cleanup(curl);
|
||||
fclose(public_key_file);
|
||||
remove(tmp_filename);
|
||||
ast_free(buf);
|
||||
curl_cb_open_socket_free(open_socket_data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
curl_cb_open_socket_free(open_socket_data);
|
||||
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
fclose(public_key_file);
|
||||
|
||||
if (http_code / 100 != 2) {
|
||||
ast_log(LOG_ERROR, "Failed to retrieve URL '%s': code %ld\n", public_cert_url, http_code);
|
||||
remove(tmp_filename);
|
||||
ast_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
serial = stir_shaken_get_serial_number_x509(tmp_filename);
|
||||
if (!ast_begins_with(buf->buf, BEGIN_CERTIFICATE_STR)) {
|
||||
ast_log(LOG_WARNING, "Certificate from %s does not begin with what we expect\n", public_cert_url);
|
||||
ast_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
serial = stir_shaken_get_serial_number_x509(buf->buf, buf->size);
|
||||
if (!serial) {
|
||||
ast_log(LOG_ERROR, "Failed to get serial from cert %s\n", tmp_filename);
|
||||
remove(tmp_filename);
|
||||
ast_log(LOG_ERROR, "Failed to get serial from CURL buffer from %s\n", public_cert_url);
|
||||
ast_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ast_asprintf(&filename, "%s/%s.pem", path, serial) < 0) {
|
||||
ast_log(LOG_ERROR, "Failed to allocate memory for new filename for temporary "
|
||||
"file %s after CURL\n", tmp_filename);
|
||||
ast_log(LOG_ERROR, "Failed to allocate memory for filename after CURL from %s\n", public_cert_url);
|
||||
ast_free(serial);
|
||||
remove(tmp_filename);
|
||||
ast_free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ast_free(serial);
|
||||
|
||||
if (rename(tmp_filename, filename)) {
|
||||
ast_log(LOG_ERROR, "Failed to rename temporary file %s to %s after CURL\n", tmp_filename, filename);
|
||||
public_key_file = fopen(filename, "w");
|
||||
if (!public_key_file) {
|
||||
ast_log(LOG_ERROR, "Failed to open file '%s' to write public key from '%s': %s (%d)\n",
|
||||
filename, public_cert_url, strerror(errno), errno);
|
||||
ast_free(buf);
|
||||
ast_free(filename);
|
||||
remove(tmp_filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fputs(buf->buf, public_key_file) == EOF) {
|
||||
ast_log(LOG_ERROR, "Failed to write string to file from URL %s\n", public_cert_url);
|
||||
fclose(public_key_file);
|
||||
ast_free(buf);
|
||||
ast_free(filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(public_key_file);
|
||||
ast_free(buf);
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
@@ -18,6 +18,8 @@
|
||||
#ifndef _STIR_SHAKEN_CURL_H
|
||||
#define _STIR_SHAKEN_CURL_H
|
||||
|
||||
struct ast_acl_list;
|
||||
|
||||
/* Forward declaration for CURL callback data */
|
||||
struct curl_cb_data;
|
||||
|
||||
@@ -66,10 +68,11 @@ char *curl_cb_data_get_expires(const struct curl_cb_data *data);
|
||||
* \param public_cert_url The public cert URL
|
||||
* \param path The path to download the file to
|
||||
* \param data The curl_cb_data
|
||||
* \param acl The ACL to use for cURL (if not NULL)
|
||||
*
|
||||
* \retval NULL on failure
|
||||
* \retval full path filename on success
|
||||
*/
|
||||
char *curl_public_key(const char *public_cert_url, const char *path, struct curl_cb_data *data);
|
||||
char *curl_public_key(const char *public_cert_url, const char *path, struct curl_cb_data *data, const struct ast_acl_list *acl);
|
||||
|
||||
#endif /* _STIR_SHAKEN_CURL_H */
|
||||
|
241
res/res_stir_shaken/profile.c
Normal file
241
res/res_stir_shaken/profile.c
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2022, Sangoma Technologies Corporation
|
||||
*
|
||||
* Ben Ford <bford@sangoma.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/sorcery.h"
|
||||
|
||||
#include "stir_shaken.h"
|
||||
#include "profile.h"
|
||||
#include "asterisk/res_stir_shaken.h"
|
||||
|
||||
#define CONFIG_TYPE "profile"
|
||||
|
||||
static void stir_shaken_profile_destructor(void *obj)
|
||||
{
|
||||
struct stir_shaken_profile *cfg = obj;
|
||||
|
||||
ast_free_acl_list(cfg->acl);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void *stir_shaken_profile_alloc(const char *name)
|
||||
{
|
||||
struct stir_shaken_profile *cfg;
|
||||
|
||||
cfg = ast_sorcery_generic_alloc(sizeof(*cfg), stir_shaken_profile_destructor);
|
||||
if (!cfg) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return cfg;
|
||||
}
|
||||
|
||||
static struct stir_shaken_profile *stir_shaken_profile_get(const char *id)
|
||||
{
|
||||
return ast_sorcery_retrieve_by_id(ast_stir_shaken_sorcery(), CONFIG_TYPE, id);
|
||||
}
|
||||
|
||||
static struct ao2_container *stir_shaken_profile_get_all(void)
|
||||
{
|
||||
return ast_sorcery_retrieve_by_fields(ast_stir_shaken_sorcery(), CONFIG_TYPE,
|
||||
AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
|
||||
}
|
||||
|
||||
struct stir_shaken_profile *ast_stir_shaken_get_profile_by_name(const char *name)
|
||||
{
|
||||
return ast_sorcery_retrieve_by_id(ast_stir_shaken_sorcery(), CONFIG_TYPE, name);
|
||||
}
|
||||
|
||||
static int stir_shaken_profile_apply(const struct ast_sorcery *sorcery, void *obj)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stir_shaken_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct stir_shaken_profile *cfg = obj;
|
||||
|
||||
if (!strcasecmp("attest", var->value)) {
|
||||
cfg->stir_shaken = STIR_SHAKEN_ATTEST;
|
||||
} else if (!strcasecmp("verify", var->value)) {
|
||||
cfg->stir_shaken = STIR_SHAKEN_VERIFY;
|
||||
} else if (!strcasecmp("on", var->value)) {
|
||||
cfg->stir_shaken = STIR_SHAKEN_ON;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "'%s' is not a valid value for option "
|
||||
"'stir_shaken' for %s %s\n",
|
||||
var->value, CONFIG_TYPE, ast_sorcery_object_get_id(cfg));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *stir_shaken_map[] = {
|
||||
[STIR_SHAKEN_ATTEST] = "attest",
|
||||
[STIR_SHAKEN_VERIFY] = "verify",
|
||||
[STIR_SHAKEN_ON] = "on",
|
||||
};
|
||||
|
||||
static int stir_shaken_to_str(const void *obj, const intptr_t *args, char **buf)
|
||||
{
|
||||
const struct stir_shaken_profile *cfg = obj;
|
||||
if (ARRAY_IN_BOUNDS(cfg->stir_shaken, stir_shaken_map)) {
|
||||
*buf = ast_strdup(stir_shaken_map[cfg->stir_shaken]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stir_shaken_acl_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
|
||||
{
|
||||
struct stir_shaken_profile *cfg = obj;
|
||||
int error = 0;
|
||||
int ignore;
|
||||
|
||||
if (ast_strlen_zero(var->value)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_append_acl(var->name, var->value, &cfg->acl, &error, &ignore);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int acl_to_str(const void *obj, const intptr_t *args, char **buf)
|
||||
{
|
||||
const struct stir_shaken_profile *cfg = obj;
|
||||
struct ast_acl_list *acl_list;
|
||||
struct ast_acl *first_acl;
|
||||
|
||||
if (cfg && !ast_acl_list_is_empty(acl_list=cfg->acl)) {
|
||||
AST_LIST_LOCK(acl_list);
|
||||
first_acl = AST_LIST_FIRST(acl_list);
|
||||
if (ast_strlen_zero(first_acl->name)) {
|
||||
*buf = "deny/permit";
|
||||
} else {
|
||||
*buf = first_acl->name;
|
||||
}
|
||||
AST_LIST_UNLOCK(acl_list);
|
||||
}
|
||||
|
||||
*buf = ast_strdup(*buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *stir_shaken_profile_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
struct stir_shaken_profile *cfg;
|
||||
|
||||
switch(cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "stir_shaken show profile";
|
||||
e->usage =
|
||||
"Usage: stir_shaken show profile <id>\n"
|
||||
" Show the stir/shaken profile settings for a given id\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
if (a->pos == 3) {
|
||||
return stir_shaken_tab_complete_name(a->word, stir_shaken_profile_get_all());
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (a->argc != 4) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
cfg = stir_shaken_profile_get(a->argv[3]);
|
||||
stir_shaken_cli_show(cfg, a, 0);
|
||||
ast_acl_output(a->fd, cfg->acl, NULL);
|
||||
ao2_cleanup(cfg);
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static char *stir_shaken_profile_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
struct ao2_container *container;
|
||||
|
||||
switch(cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "stir_shaken show profiles";
|
||||
e->usage =
|
||||
"Usage: stir_shaken show profiles\n"
|
||||
" Show all profiles for stir/shaken\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (a->argc != 3) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
container = stir_shaken_profile_get_all();
|
||||
if (!container || ao2_container_count(container) == 0) {
|
||||
ast_cli(a->fd, "No stir/shaken ACLs found\n");
|
||||
ao2_cleanup(container);
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
ao2_callback(container, OBJ_NODATA, stir_shaken_cli_show, a);
|
||||
ao2_ref(container, -1);
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static struct ast_cli_entry stir_shaken_profile_cli[] = {
|
||||
AST_CLI_DEFINE(stir_shaken_profile_show, "Show stir/shaken profile by id"),
|
||||
AST_CLI_DEFINE(stir_shaken_profile_show_all, "Show all stir/shaken profiles"),
|
||||
};
|
||||
|
||||
int stir_shaken_profile_unload(void)
|
||||
{
|
||||
ast_cli_unregister_multiple(stir_shaken_profile_cli,
|
||||
ARRAY_LEN(stir_shaken_profile_cli));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int stir_shaken_profile_load(void)
|
||||
{
|
||||
struct ast_sorcery *sorcery = ast_stir_shaken_sorcery();
|
||||
|
||||
ast_sorcery_apply_default(sorcery, CONFIG_TYPE, "config", "stir_shaken.conf,criteria=type=profile");
|
||||
|
||||
if (ast_sorcery_object_register(sorcery, CONFIG_TYPE, stir_shaken_profile_alloc,
|
||||
NULL, stir_shaken_profile_apply)) {
|
||||
ast_log(LOG_ERROR, "stir/shaken - failed to register '%s' sorcery object\n", CONFIG_TYPE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_sorcery_object_field_register(sorcery, CONFIG_TYPE, "type", "", OPT_NOOP_T, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "stir_shaken", "on", stir_shaken_handler, stir_shaken_to_str, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "deny", "", stir_shaken_acl_handler, NULL, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "permit", "", stir_shaken_acl_handler, NULL, NULL, 0, 0);
|
||||
ast_sorcery_object_field_register_custom(sorcery, CONFIG_TYPE, "acllist", "", stir_shaken_acl_handler, acl_to_str, NULL, 0, 0);
|
||||
|
||||
ast_cli_register_multiple(stir_shaken_profile_cli,
|
||||
ARRAY_LEN(stir_shaken_profile_cli));
|
||||
|
||||
return 0;
|
||||
}
|
39
res/res_stir_shaken/profile.h
Normal file
39
res/res_stir_shaken/profile.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2022, Sangoma Technologies Corporation
|
||||
*
|
||||
* Ben Ford <bford@sangoma.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
#ifndef _STIR_SHAKEN_PROFILE_H
|
||||
#define _STIR_SHAKEN_PROFILE_H
|
||||
|
||||
#include "profile_private.h"
|
||||
|
||||
struct stir_shaken_profile *ast_stir_shaken_get_profile_by_name(const char *name);
|
||||
|
||||
/*!
|
||||
* \brief Load time initialization for the stir/shaken 'profile' object
|
||||
*
|
||||
* \retval 0 on success, -1 on error
|
||||
*/
|
||||
int stir_shaken_profile_load(void);
|
||||
|
||||
/*!
|
||||
* \brief Unload time cleanup for the stir/shaken 'profile'
|
||||
*
|
||||
* \retval 0 on success, -1 on error
|
||||
*/
|
||||
int stir_shaken_profile_unload(void);
|
||||
|
||||
#endif /* _STIR_SHAKEN_PROFILE_H */
|
40
res/res_stir_shaken/profile_private.h
Normal file
40
res/res_stir_shaken/profile_private.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Asterisk -- An open source telephony toolkit.
|
||||
*
|
||||
* Copyright (C) 2022, Sangoma Technologies Corporation
|
||||
*
|
||||
* Ben Ford <bford@sangoma.com>
|
||||
*
|
||||
* See http://www.asterisk.org for more information about
|
||||
* the Asterisk project. Please do not directly contact
|
||||
* any of the maintainers of this project for assistance;
|
||||
* the project provides a web site, mailing lists and IRC
|
||||
* channels for your use.
|
||||
*
|
||||
* This program is free software, distributed under the terms of
|
||||
* the GNU General Public License Version 2. See the LICENSE file
|
||||
* at the top of the source tree.
|
||||
*/
|
||||
#ifndef _STIR_SHAKEN_PROFILE_PRIVATE_H
|
||||
#define _STIR_SHAKEN_PROFILE_PRIVATE_H
|
||||
|
||||
#include "asterisk/sorcery.h"
|
||||
|
||||
#include "asterisk/acl.h"
|
||||
|
||||
enum stir_shaken_profile_behavior {
|
||||
/*! Only do STIR/SHAKEN attestation */
|
||||
STIR_SHAKEN_ATTEST = 1,
|
||||
/*! Only do STIR/SHAKEN verification */
|
||||
STIR_SHAKEN_VERIFY = 2,
|
||||
/*! Do STIR/SHAKEN attestation and verification */
|
||||
STIR_SHAKEN_ON = 3,
|
||||
};
|
||||
|
||||
struct stir_shaken_profile {
|
||||
SORCERY_OBJECT(details);
|
||||
unsigned int stir_shaken;
|
||||
struct ast_acl_list *acl;
|
||||
};
|
||||
|
||||
#endif /* _STIR_SHAKEN_PROFILE_PRIVATE_H */
|
@@ -137,41 +137,35 @@ EVP_PKEY *stir_shaken_read_key(const char *path, int priv)
|
||||
return key;
|
||||
}
|
||||
|
||||
char *stir_shaken_get_serial_number_x509(const char *path)
|
||||
char *stir_shaken_get_serial_number_x509(const char *buf, size_t buf_size)
|
||||
{
|
||||
FILE *fp;
|
||||
BIO *certBIO;
|
||||
X509 *cert;
|
||||
ASN1_INTEGER *serial;
|
||||
BIGNUM *bignum;
|
||||
char *serial_hex;
|
||||
char *ret;
|
||||
|
||||
fp = fopen(path, "r");
|
||||
if (!fp) {
|
||||
ast_log(LOG_ERROR, "Failed to open file %s\n", path);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cert = PEM_read_X509(fp, NULL, NULL, NULL);
|
||||
certBIO = BIO_new(BIO_s_mem());
|
||||
BIO_write(certBIO, buf, buf_size);
|
||||
cert = PEM_read_bio_X509(certBIO, NULL, NULL, NULL);
|
||||
BIO_free(certBIO);
|
||||
if (!cert) {
|
||||
ast_log(LOG_ERROR, "Failed to read X.509 cert from file %s\n", path);
|
||||
fclose(fp);
|
||||
ast_log(LOG_ERROR, "Failed to read X.509 cert from buffer\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
serial = X509_get_serialNumber(cert);
|
||||
if (!serial) {
|
||||
ast_log(LOG_ERROR, "Failed to get serial number from certificate %s\n", path);
|
||||
ast_log(LOG_ERROR, "Failed to get serial number from certificate\n");
|
||||
X509_free(cert);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bignum = ASN1_INTEGER_to_BN(serial, NULL);
|
||||
if (bignum == NULL) {
|
||||
ast_log(LOG_ERROR, "Failed to convert serial to bignum for certificate %s\n", path);
|
||||
ast_log(LOG_ERROR, "Failed to convert serial to bignum for certificate\n");
|
||||
X509_free(cert);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -181,18 +175,17 @@ char *stir_shaken_get_serial_number_x509(const char *path)
|
||||
*/
|
||||
serial_hex = BN_bn2hex(bignum);
|
||||
X509_free(cert);
|
||||
fclose(fp);
|
||||
BN_free(bignum);
|
||||
|
||||
if (!serial_hex) {
|
||||
ast_log(LOG_ERROR, "Failed to convert bignum to hex for certificate %s\n", path);
|
||||
ast_log(LOG_ERROR, "Failed to convert bignum to hex for certificate\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret = ast_strdup(serial_hex);
|
||||
OPENSSL_free(serial_hex);
|
||||
if (!ret) {
|
||||
ast_log(LOG_ERROR, "Failed to dup serial from openssl for certificate %s\n", path);
|
||||
ast_log(LOG_ERROR, "Failed to dup serial from openssl for certificate\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@@ -53,15 +53,16 @@ char *stir_shaken_tab_complete_name(const char *word, struct ao2_container *cont
|
||||
EVP_PKEY *stir_shaken_read_key(const char *path, int priv);
|
||||
|
||||
/*!
|
||||
* \brief Gets the serial number in hex form from the X509 certificate at path
|
||||
* \brief Gets the serial number in hex form from the buffer (for X509)
|
||||
*
|
||||
* \note The returned string will need to be freed by the caller
|
||||
*
|
||||
* \param path The full path of the X509 certificate
|
||||
* \param buf The BASE64 encoded buffer
|
||||
* \param buf_size The size of the data in buf
|
||||
*
|
||||
* \retval NULL on failure
|
||||
* \retval serial number on success
|
||||
*/
|
||||
char *stir_shaken_get_serial_number_x509(const char *path);
|
||||
char *stir_shaken_get_serial_number_x509(const char *buf, size_t buf_size);
|
||||
|
||||
#endif /* _STIR_SHAKEN_H */
|
||||
|
Reference in New Issue
Block a user