├── .dockerignore ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── appveyor.yml ├── assets ├── css │ ├── login.css │ └── vis.min.css ├── index.htm ├── js │ ├── app.js │ └── vis.min.js └── report.htm ├── cert ├── ca-cert.pem ├── server-cert.pem └── server-key.pem ├── conf └── dbshield.yml ├── dbshield ├── config │ ├── config.go │ └── config_test.go ├── dbms │ ├── db2.go │ ├── db2_test.go │ ├── io.go │ ├── io_test.go │ ├── mssql.go │ ├── mssql_test.go │ ├── mysql.go │ ├── mysql_test.go │ ├── oracle.go │ ├── oracle_test.go │ ├── postgres.go │ ├── postgres_test.go │ ├── utils.go │ └── utils_test.go ├── dbshield.go ├── dbshield_test.go ├── httpserver │ ├── server.go │ └── server_test.go ├── logger │ ├── log.go │ └── log_test.go ├── sql │ ├── sql.go │ └── sql_test.go ├── training │ ├── training.go │ └── training_test.go ├── utils.go ├── utils │ ├── action.go │ ├── action_test.go │ └── utils.go └── utils_test.go ├── main.go ├── main_test.go ├── misc ├── Diagrams.xml ├── demo.gif ├── graph.png ├── how_01.png ├── how_02.png ├── how_03.png ├── no.png ├── scripts │ ├── README.md │ └── pcap2bytearray.go └── yes.png └── test.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .gitignore 3 | misc 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | os: 4 | - linux 5 | 6 | go: 7 | - 1.5 8 | - 1.6 9 | - tip 10 | 11 | before_install: 12 | - sudo apt-get -qq update 13 | - sudo apt-get install -y libpcap-dev 14 | 15 | script: 16 | - go vet ./... 17 | - ./test.sh 18 | 19 | after_success: 20 | - bash <(curl -s https://codecov.io/bash) 21 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at ghotbi.nima@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thank you for considering contributing! There are many ways you can help. 4 | 5 | ## Issues 6 | 7 | File an issue if you think you've found a bug. Be sure to describe 8 | 9 | 1. How can it be reproduced? 10 | 2. What did you expect? 11 | 3. What actually occurred? 12 | 4. Go version, platform, etc. if possibly relevant. 13 | 14 | ## Docs 15 | 16 | Godocs documentation, READMEs, and examples are extremely imporant. Please help improve them and if you find a typo or notice a problem, please send a fix or say something. 17 | 18 | ## Submitting Patches 19 | 20 | Patches for fixes, features, and improvements are accepted through pull requests. 21 | 22 | * Sign [CIA](https://cla-assistant.io/nim4/DBShield) 23 | * Code must be go fmt'd. 24 | * Code must pass golint and vet checks. 25 | * Exported methods must be documented. 26 | * Include tests to improve coverage and prevent regressions. 27 | * Squash changes into a single commit per feature/fix. 28 | * Write good commit messages. 29 | 30 | Please ask before embarking on a large improvement so you're not disappointed if it does not align with the goals of the project. 31 | 32 | ## Feature Requests 33 | 34 | Make the case for a feature via an issue with a good title. The feature should be discussed and given a target inclusion milestone or closed. 35 | 36 | ## Contact 37 | 38 | If you'd like to ask about something directly, tweet at [@Nilvl4](https://twitter.com/nilvl4) or email ghotbi.nima at gmail. 39 | 40 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.6.3 2 | MAINTAINER Nima Ghotbi 3 | 4 | ENV GOPATH /go 5 | 6 | COPY . /go/src/github.com/nim4/DBShield 7 | WORKDIR /go/src/github.com/nim4/DBShield 8 | COPY conf/dbshield.yml /etc/dbshield.yml 9 | 10 | RUN openssl genrsa -out cert/server-key.pem 2048 11 | RUN openssl req -new -x509 -sha256 -key cert/server-key.pem -out cert/server-cert.pem -days 3650 -subj '/CN=DBShield/O=DBShield/C=TR' 12 | 13 | RUN go get 14 | RUN go build 15 | ENTRYPOINT /go/bin/DBShield 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2017 Nima Ghotbi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Linux](https://travis-ci.org/nim4/DBShield.svg?branch=master "Linux")](https://travis-ci.org/nim4/DBShield) 2 | [![Windows](https://ci.appveyor.com/api/projects/status/github/nim4/DBShield?branch=master&svg=true "Windows")](https://ci.appveyor.com/project/nim4/DBShield/branch/master) 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/nim4/DBShield)](https://goreportcard.com/report/github.com/nim4/DBShield) 4 | [![codecov](https://codecov.io/gh/nim4/DBShield/branch/master/graph/badge.svg)](https://codecov.io/gh/nim4/DBShield) 5 | [![Dev chat](https://img.shields.io/badge/gitter-chat-20cc20.svg "Dev chat")](https://gitter.im/DBShield/Lobby) 6 | [![GoDoc](https://godoc.org/github.com/nim4/DBShield?status.svg)](https://godoc.org/github.com/nim4/DBShield) 7 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/nim4/DBShield/master/LICENSE) 8 | # DBShield 9 | 10 | Protects your data by inspecting incoming queries from your application server and rejecting abnormal ones. 11 | 12 | 13 | --- 14 | ## How it works? 15 | 16 | For example, this is how web server normally interacts with database server: 17 | 18 | ![Sample Web Server and DB](https://raw.githubusercontent.com/nim4/DBShield/master/misc/how_01.png) 19 | 20 | By adding DBShield in front of database server we can protect it against abnormal queries. To detect abnormal queries we first run DBShield in learning mode. Learning mode lets any query pass but it records information about it (pattern, username, time and source) into the internal database. 21 | 22 | ![Learning mode](https://raw.githubusercontent.com/nim4/DBShield/master/misc/how_02.png) 23 | 24 | 25 | After collecting enough patterns we can run DBShield in protect mode. Protect mode can distinguish abnormal query pattern, user and source and take action based on configurations. 26 | 27 | ![Protect mode](https://raw.githubusercontent.com/nim4/DBShield/master/misc/how_03.png) 28 | 29 | --- 30 | ## Demo 31 | For demo, we are using [sqlmap](https://github.com/sqlmapproject/sqlmap)(automatic SQL injection and database takeover tool) to exploit the SQL injection vulnerability at `user.php` 32 | 33 | In the first scenario, the sqlmap successfully exploits the SQL injection when web application connected directly to the database(MySQL), In the second scenario, we modify the `user.php` so DBShield gets between the web application and database which will drop the injection attempt and make sqlmap fail. 34 | 35 | ![Demo](misc/demo.gif) 36 | --- 37 | 38 | ## Sample Outputs 39 | 40 | **CLI** 41 | 42 | ``` 43 | $ go run main.go 44 | 2016/10/15 16:25:31 [INFO] Config file: /etc/dbshield.yml 45 | 2016/10/15 16:25:31 [INFO] Internal DB: /tmp/model/10.0.0.21_postgres.db 46 | 2016/10/15 16:25:31 [INFO] Listening: 0.0.0.0:5000 47 | 2016/10/15 16:25:31 [INFO] Backend: postgres (10.0.0.21:5432) 48 | 2016/10/15 16:25:31 [INFO] Protect: true 49 | 2016/10/15 16:25:31 [INFO] Web interface on https://127.0.0.1:8070/ 50 | 2016/10/15 16:25:33 [INFO] Connected from: 10.0.0.20:35910 51 | 2016/10/15 16:25:33 [INFO] Connected to: 10.0.0.21:5432 52 | 2016/10/15 16:25:33 [INFO] SSL connection 53 | 2016/10/15 16:25:34 [DEBUG] Client handshake done 54 | 2016/10/15 16:25:34 [DEBUG] Server handshake done 55 | 2016/10/15 16:25:34 [INFO] User: postgres 56 | 2016/10/15 16:25:34 [INFO] Database: test 57 | 2016/10/15 16:25:34 [INFO] Query: SELECT * FROM stocks where id=-1 or 1=1 58 | 2016/10/15 16:25:34 [WARN] Pattern not found: [53 55 51 52 55 52 50 53 55 51 53 49 115 116 111 99 107 115 53 55 51 53 50 105 100 54 49 52 53 53 55 51 55 57 53 55 52 48 52 53 55 51 55 57 54 49 53 55 51 55 57] (SELECT * FROM stocks where id=-1 or 1=1) 59 | 2016/10/15 16:25:34 [WARN] Dropping connection 60 | ``` 61 | 62 | 63 | **Web Interface** 64 | 65 | ![Web UI](https://raw.githubusercontent.com/nim4/DBShield/master/misc/graph.png) 66 | 67 | --- 68 | ## Installation 69 | 70 | Get it 71 | ``` 72 | $ go get -u github.com/nim4/DBShield 73 | ``` 74 | 75 | Then you can see help using "-h" argument: 76 | ``` 77 | $ $GOPATH/bin/DBShield -h 78 | DBShield 1.0.0-beta3 79 | Usage of DBShield: 80 | -a get list of abnormal queries 81 | -c file 82 | config file (default "/etc/dbshield.yml") 83 | -h show help 84 | -k show parsed config and exit 85 | -l get list of captured patterns 86 | -version 87 | show version 88 | ``` 89 | 90 | and run it with your configuration, like: 91 | ``` 92 | $ $GOPATH/bin/DBShield -c config.yml 93 | ``` 94 | see [sample configuration file](https://github.com/nim4/DBShield/blob/master/conf/dbshield.yml) 95 | 96 | 97 | >:warning: **WARNING:** 98 | > Do NOT use default certificates in production environments! 99 | 100 | 101 | --- 102 | ## Supports: 103 | 104 | | Database | Protect | SSL | 105 | |:------------:|:-------:|:---:| 106 | | **DB2** | ![Yes][YesImg] | ![No][NoImg] | 107 | | **MariaDB** | ![Yes][YesImg] | ![Yes][YesImg] | 108 | | **MySQL** | ![Yes][YesImg] | ![Yes][YesImg] | 109 | | **Oracle** | ![Yes][YesImg] | ![No][NoImg] | 110 | | **Postgres** | ![Yes][YesImg] | ![Yes][YesImg] | 111 | 112 | --- 113 | ## To Do 114 | 115 | (Sorted by priority) 116 | 117 | - Improve documentation 118 | - Add Microsoft SQL Server 119 | - Add more command-line arguments 120 | - Get 90% test coverage 121 | - Support Oracle SSL 122 | 123 | [YesImg]: https://raw.githubusercontent.com/nim4/DBShield/master/misc/yes.png 124 | [NoImg]: https://raw.githubusercontent.com/nim4/DBShield/master/misc/no.png 125 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | os: Windows Server 2012 R2 4 | 5 | clone_folder: c:\gopath\src\github.com\nim4\DBShield 6 | shallow_clone: true 7 | 8 | environment: 9 | GOPATH: c:\gopath 10 | 11 | install: 12 | - echo %PATH% 13 | - go version 14 | - go env 15 | - go get -v -d -t ./... 16 | 17 | build_script: 18 | - go install -v ./... 19 | 20 | test_script: 21 | - go test -v ./... 22 | -------------------------------------------------------------------------------- /assets/css/login.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | width: 100%; 3 | height: 100%; 4 | font-family: "Helvetica Neue", Helvetica, sans-serif; 5 | color: #444; 6 | -webkit-font-smoothing: antialiased; 7 | background: #f0f0f0; 8 | } 9 | 10 | .header { 11 | text-align: center; 12 | margin-top: 5%; 13 | margin-bottom: -5px; 14 | } 15 | 16 | #container { 17 | position: fixed; 18 | width: 340px; 19 | top: 50%; 20 | left: 50%; 21 | margin-top: -140px; 22 | margin-left: -170px; 23 | background: #fff; 24 | border-radius: 3px; 25 | border: 1px solid #ccc; 26 | box-shadow: 0 1px 2px rgba(0, 0, 0, .1); 27 | } 28 | 29 | form { 30 | margin: 0 auto; 31 | margin-top: 20px; 32 | } 33 | 34 | label { 35 | color: #555; 36 | display: inline-block; 37 | margin-left: 18px; 38 | padding-top: 10px; 39 | font-size: 14px; 40 | } 41 | 42 | p a { 43 | font-size: 11px; 44 | color: #aaa; 45 | float: right; 46 | margin-top: -13px; 47 | margin-right: 20px; 48 | -webkit-transition: all .4s ease; 49 | -moz-transition: all .4s ease; 50 | transition: all .4s ease; 51 | } 52 | 53 | p a:hover { 54 | color: #555; 55 | } 56 | 57 | input { 58 | font-family: "Helvetica Neue", Helvetica, sans-serif; 59 | font-size: 12px; 60 | outline: none; 61 | } 62 | 63 | input[type=text], input[type=password] { 64 | color: #777; 65 | padding-left: 10px; 66 | margin: 10px; 67 | margin-top: 12px; 68 | margin-left: 18px; 69 | width: 290px; 70 | height: 35px; 71 | border: 1px solid #c7d0d2; 72 | border-radius: 2px; 73 | box-shadow: inset 0 1.5px 3px rgba(190, 190, 190, .4), 0 0 0 5px #f5f7f8; 74 | -webkit-transition: all .4s ease; 75 | -moz-transition: all .4s ease; 76 | transition: all .4s ease; 77 | } 78 | 79 | input[type=text]:hover, input[type=password]:hover { 80 | border: 1px solid #b6bfc0; 81 | box-shadow: inset 0 1.5px 3px rgba(190, 190, 190, .7), 0 0 0 5px #f5f7f8; 82 | } 83 | 84 | input[type=text]:focus, input[type=password]:focus { 85 | border: 1px solid #a8c9e4; 86 | box-shadow: inset 0 1.5px 3px rgba(190, 190, 190, .4), 0 0 0 5px #e6f2f9; 87 | } 88 | 89 | #lower { 90 | background: #ecf2f5; 91 | width: 100%; 92 | height: 69px; 93 | margin-top: 20px; 94 | box-shadow: inset 0 1px 1px #fff; 95 | border-top: 1px solid #ccc; 96 | border-bottom-right-radius: 3px; 97 | border-bottom-left-radius: 3px; 98 | } 99 | 100 | input[type=checkbox] { 101 | margin-left: 20px; 102 | margin-top: 30px; 103 | } 104 | 105 | .check { 106 | margin-left: 3px; 107 | font-size: 11px; 108 | color: #444; 109 | text-shadow: 0 1px 0 #fff; 110 | } 111 | 112 | input[type=submit] { 113 | float: right; 114 | margin-right: 20px; 115 | margin-top: 20px; 116 | width: 80px; 117 | height: 30px; 118 | font-size: 14px; 119 | font-weight: bold; 120 | color: #fff; 121 | background-color: #acd6ef; 122 | /*IE fallback*/ 123 | background-image: -webkit-gradient(linear, left top, left bottom, from(#acd6ef), to(#6ec2e8)); 124 | background-image: -moz-linear-gradient(top left 90deg, #acd6ef 0%, #6ec2e8 100%); 125 | background-image: linear-gradient(top left 90deg, #acd6ef 0%, #6ec2e8 100%); 126 | border-radius: 30px; 127 | border: 1px solid #66add6; 128 | box-shadow: 0 1px 2px rgba(0, 0, 0, .3), inset 0 1px 0 rgba(255, 255, 255, .5); 129 | cursor: pointer; 130 | } 131 | 132 | input[type=submit]:hover { 133 | background-image: -webkit-gradient(linear, left top, left bottom, from(#b6e2ff), to(#6ec2e8)); 134 | background-image: -moz-linear-gradient(top left 90deg, #b6e2ff 0%, #6ec2e8 100%); 135 | background-image: linear-gradient(top left 90deg, #b6e2ff 0%, #6ec2e8 100%); 136 | } 137 | 138 | input[type=submit]:active { 139 | background-image: -webkit-gradient(linear, left top, left bottom, from(#6ec2e8), to(#b6e2ff)); 140 | background-image: -moz-linear-gradient(top left 90deg, #6ec2e8 0%, #b6e2ff 100%); 141 | background-image: linear-gradient(top left 90deg, #6ec2e8 0%, #b6e2ff 100%); 142 | } 143 | -------------------------------------------------------------------------------- /assets/css/vis.min.css: -------------------------------------------------------------------------------- 1 | .vis-background,.vis-labelset,.vis-timeline{overflow:hidden}.vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration-wrapper::after{clear:both;content:"";display:block}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:120px;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;padding:1px;margin:0;pointer-events:none}.vis-panel,.vis-timeline{padding:0;box-sizing:border-box}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:rgba(0,0,0,0);width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#3876c2', endColorstr='#385380', GradientType=0 );box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:0}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#9d9d9d', endColorstr='#c8c8c8', GradientType=0 )}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#dedede', endColorstr='#c8c8c8', GradientType=0 );border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:#fff solid 1px;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0);border-left-color:rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0);border-left-color:#f2faff;border-width:12px;margin-top:-12px}.vis-timeline{position:relative;border:1px solid #bfbfbf;margin:0}.vis-panel{position:absolute;margin:0}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right,.vis-panel.vis-top{border:1px #bfbfbf}.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-top{border-left-style:solid;border-right-style:solid}.vis-panel>.vis-content{position:relative}.vis-panel .vis-shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis-itemset,.vis-labelset,.vis-labelset .vis-label{position:relative;box-sizing:border-box}.vis-panel .vis-shadow.vis-top{top:-1px;left:0}.vis-panel .vis-shadow.vis-bottom{bottom:-1px;left:0}.vis-labelset .vis-label{left:0;top:0;width:100%;color:#4d4d4d;border-bottom:1px solid #bfbfbf}.vis-labelset .vis-label.draggable{cursor:pointer}.vis-labelset .vis-label:last-child{border-bottom:none}.vis-labelset .vis-label .vis-inner{display:inline-block;padding:5px}.vis-labelset .vis-label .vis-inner.vis-hidden{padding:0}.vis-itemset{padding:0;margin:0}.vis-itemset .vis-background,.vis-itemset .vis-foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis-axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis-foreground .vis-group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis-foreground .vis-group:last-child{border-bottom:none}.vis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-item{position:absolute;color:#1A1A1A;border-color:#97B0F8;border-width:1px;background-color:#D5DDF6;display:inline-block}.vis-item.vis-point.vis-selected,.vis-item.vis-selected{background-color:#FFF785}.vis-item.vis-selected{border-color:#FFC200;z-index:2}.vis-editable.vis-selected{cursor:move}.vis-item.vis-box{text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-point{background:0 0}.vis-item.vis-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis-item.vis-range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis-item.vis-background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis-item .vis-item-overflow{position:relative;width:100%;height:100%;padding:0;margin:0;overflow:hidden}.vis-item .vis-delete,.vis-item .vis-delete-rtl{background:url(img/timeline/delete.png) center no-repeat;height:24px;top:-4px;cursor:pointer}.vis-item.vis-range .vis-item-content{position:relative;display:inline-block}.vis-item.vis-background .vis-item-content{position:absolute;display:inline-block}.vis-item.vis-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item .vis-item-content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis-item .vis-delete{position:absolute;width:24px;right:-24px}.vis-item .vis-delete-rtl{position:absolute;width:24px;left:-24px}.vis-item.vis-range .vis-drag-left{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;left:-4px;cursor:w-resize}.vis-item.vis-range .vis-drag-right{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;right:-4px;cursor:e-resize}.vis-range.vis-item.vis-readonly .vis-drag-left,.vis-range.vis-item.vis-readonly .vis-drag-right{cursor:auto}.vis-time-axis{position:relative;overflow:hidden}.vis-time-axis.vis-foreground{top:0;left:0;width:100%}.vis-time-axis.vis-background{position:absolute;top:0;left:0;width:100%;height:100%}.vis-time-axis .vis-text{position:absolute;color:#4d4d4d;padding:3px;overflow:hidden;box-sizing:border-box;white-space:nowrap}.vis-time-axis .vis-text.vis-measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis-time-axis .vis-grid.vis-vertical{position:absolute;border-left:1px solid}.vis-time-axis .vis-grid.vis-vertical-rtl{position:absolute;border-right:1px solid}.vis-time-axis .vis-grid.vis-minor{border-color:#e5e5e5}.vis-time-axis .vis-grid.vis-major{border-color:#bfbfbf}.vis-current-time{background-color:#FF7F6E;width:2px;z-index:1}.vis-custom-time{background-color:#6E94FF;width:2px;cursor:move;z-index:1}div.vis-network div.vis-close,div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-webkit-touch-callout:none;-khtml-user-select:none}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-minor{border-color:#e5e5e5}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-major{border-color:#bfbfbf}.vis-data-axis .vis-y-axis.vis-major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-major.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-minor.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis-data-axis .vis-y-axis.vis-title.vis-measure{padding:0;margin:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title.vis-left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis-data-axis .vis-y-axis.vis-title.vis-right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis-legend{background-color:rgba(247,252,255,.65);padding:5px;border:1px solid #b3b3b3;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis-legend-text{white-space:nowrap;display:inline-block}.vis-graph-group0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis-graph-group1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis-graph-group2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis-graph-group3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis-graph-group4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis-graph-group5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis-graph-group6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis-graph-group7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis-graph-group8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis-graph-group9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis-timeline .vis-fill{fill-opacity:.1;stroke:none}.vis-timeline .vis-bar{fill-opacity:.5;stroke-width:1px}.vis-timeline .vis-point{stroke-width:2px;fill-opacity:1}.vis-timeline .vis-legend-background{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis-timeline .vis-outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis-timeline .vis-icon-fill{fill-opacity:.3;stroke:none}div.vis-network div.vis-manipulation{border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#fcfcfc', GradientType=0 );padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network div.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);user-select:none}div.vis-network div.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{float:left;font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;padding:0 8px;user-select:none}div.vis-network div.vis-manipulation div.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation div.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation div.vis-button.vis-back{background-image:url(img/network/backIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-button.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-button.vis-none{padding:0}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation div.vis-button.vis-add{background-image:url(img/network/addNodeIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit,div.vis-network div.vis-manipulation div.vis-button.vis-edit{background-image:url(img/network/editIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation div.vis-button.vis-connect{background-image:url(img/network/connectIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-delete{background-image:url(img/network/deleteIcon.png)}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px}div.vis-network-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px}div.vis-color-picker{position:absolute;top:0;left:30px;margin-top:-140px;margin-left:30px;width:310px;height:444px;z-index:1;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5) 0 0 10px 0}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow::after,div.vis-color-picker div.vis-arrow::before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:rgba(255,255,255,0);border-right-color:#fff;border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313', GradientType=0 )}div.vis-color-picker div.vis-initial-color,div.vis-color-picker div.vis-new-color{width:140px;height:20px;top:380px;font-size:10px;color:rgba(0,0,0,.4);line-height:20px;position:absolute;vertical-align:middle}div.vis-color-picker div.vis-new-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:159px;text-align:right;padding-right:2px}div.vis-color-picker div.vis-initial-color{border:1px solid rgba(0,0,0,.1);border-radius:5px;left:10px;text-align:left;padding-left:2px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px} -------------------------------------------------------------------------------- /assets/index.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Login | DBShield 5 | 6 | 7 | 8 | 9 | 10 |

DBShield

11 | 12 |
13 |
14 | 15 | 16 |
17 | 18 |
19 |
20 |
21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /assets/js/app.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // Graph Labels 3 | var names = ['Queries', 'Abnormal']; 4 | var groups = new vis.DataSet(); 5 | 6 | //Initialize Groups 7 | groups.add({ 8 | id: 0, 9 | content: names[0], 10 | options: { 11 | drawPoints: false, 12 | interpolation: false 13 | } 14 | }); 15 | 16 | groups.add({ 17 | id: 1, 18 | content: names[1], 19 | options: { 20 | drawPoints: false, 21 | interpolation: false 22 | } 23 | }); 24 | 25 | // Chart id in Body 26 | var container = document.getElementById('visualization'); 27 | var dataset = new vis.DataSet(); 28 | 29 | //Option For Group 30 | var options = { 31 | dataAxis: { 32 | left: { 33 | range: { 34 | min: 0 35 | }, 36 | }, 37 | showMinorLabels: false 38 | }, 39 | drawPoints: false, 40 | legend: true, 41 | start: vis.moment().add(-30, 'seconds'), 42 | end: vis.moment(), 43 | }; 44 | 45 | 46 | var graph2d = new vis.Graph2d(container, dataset, groups, options); 47 | 48 | //Initilase Queries & Abnormal Values 49 | var yValuesOld = { 50 | Total: 0, 51 | Abnormal: 0 52 | } 53 | var yValues = { 54 | Total: 0, 55 | Abnormal: 0 56 | } 57 | 58 | //Return diff of last Two Queries 59 | function yQueries() { 60 | return yValues["Total"] - yValuesOld["Total"]; 61 | } 62 | 63 | //Return diff of last Two Abnormals 64 | function yAbnormal() { 65 | return yValues["Abnormal"] - yValuesOld["Abnormal"]; 66 | } 67 | 68 | function renderStep() { 69 | // move the window (you can think of different strategies). 70 | var now = vis.moment(); 71 | var range = graph2d.getWindow(); 72 | var interval = range.end - range.start; 73 | 74 | // move the window 90% to the left when now is larger than the end of the window 75 | if (now > range.end) { 76 | //graph2d.setWindow(now - interval, now, {animation: true}); 77 | graph2d.setWindow(now - 0.1 * interval, now + 0.9 * interval, { 78 | animation: true 79 | }); 80 | } 81 | } 82 | 83 | 84 | //Add datapoint to the graph 85 | function addDataPoint() { 86 | var now = vis.moment(); 87 | dataset.add([{ 88 | x: now, 89 | y: yQueries(), 90 | group: 0 91 | }, { 92 | x: now, 93 | y: yAbnormal(), 94 | group: 1 95 | }]); 96 | setTimeout(addDataPoint, 1000); 97 | } 98 | 99 | //Get Value from Api 100 | function ajax(init) { 101 | var xhttp = new XMLHttpRequest(); 102 | xhttp.onreadystatechange = function() { 103 | if (this.readyState == 4 && this.status == 200) { 104 | yValuesOld = yValues; 105 | yValues = JSON.parse(this.responseText); 106 | if (init) { 107 | // To filter the first request 108 | yValuesOld = yValues; 109 | } 110 | renderStep() 111 | } 112 | }; 113 | xhttp.onerror = function() { 114 | yValues = { 115 | Total: 0, 116 | Abnormal: 0 117 | } 118 | } 119 | xhttp.open("GET", '/api?_=' + new Date().getTime(), true); 120 | xhttp.send(); 121 | setTimeout(function() { 122 | ajax(false) 123 | }, 1000); 124 | } 125 | 126 | addDataPoint(); 127 | ajax(true); 128 | -------------------------------------------------------------------------------- /assets/report.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Realtime Graph | DBShield 8 | 9 | 10 | 11 | 50 | 51 | 52 | 53 |
54 | 55 |
56 |
57 |
58 | 59 |
60 |
61 |
62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /cert/ca-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDczCCAlugAwIBAgIJAIk5S9+zF/T2MA0GCSqGSIb3DQEBBQUAMFAxCzAJBgNV 3 | BAYTAlRSMQ4wDAYDVQQIDAVJem1pcjEOMAwGA1UEBwwFSXptaXIxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xNjA5MDYwNTUxMjVaFw0yNjA5 5 | MDQwNTUxMjVaMFAxCzAJBgNVBAYTAlRSMQ4wDAYDVQQIDAVJem1pcjEOMAwGA1UE 6 | BwwFSXptaXIxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIw 7 | DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANbDmeM8r3aFuLHuzmUJyzpwBb6B 8 | lDrfx7WaCHNcXGyIWHZpDZfsQFIf1VpEawAaI/gyOU1v673ggD4AxC1DLDfFCC3X 9 | ofCJZtzTD0Gz81KoZbTfiZ1JGMK645z9gw+zRs7qTWPDshQkJoHdg3El+q5NX3DW 10 | BLTTY7iyva1t0sRja9mp4bjUAoJu2qppioo27A5/jM3JeEUik65x01bqI9xsKmk+ 11 | 0JYlDzjdUP4DL36e2gvQ1CdhblOMGRhptNKcMiaWxa5K27z1xFbqydXF11qXvGBT 12 | YeSijpjILTcPHnxKULCO80EXdfZ2pWy1xdM4AykZYfnEYBEwxeVL5a/U8ScCAwEA 13 | AaNQME4wHQYDVR0OBBYEFH/2iaPHtRRetfXlXWGLoj2EkvkzMB8GA1UdIwQYMBaA 14 | FH/2iaPHtRRetfXlXWGLoj2EkvkzMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF 15 | BQADggEBAByvaFb3NQoTZ9I7gKT0aRBR6EdX7mG2lPIWPfLrq66rhVt0OaYS9Dwl 16 | AQtx757Q93YSpTPF3Rak/mXci/BcHtLUcqVgAHSdB7xVkaUouhubUIxdaG7jQONe 17 | eThyykCBv459iJ4CiKz09Bd0lHC7BFMMCsKIrKWFXrNlumNOpfanBLCZR9I/LwVu 18 | Uei3hy+1GFCHiYHSYrG+depg+S4CKw6UHLrvmqJE+pXDbEVQIwhBrCXeKDl/RHIt 19 | 3zwJWIWMpxjoznwsoAvH4Gd6WAQpPCfSfyCy1K7MOFw+Sts3uZ4Xgs8/NHJtZr1m 20 | yHDAciWLkNLSNy+rMAuPoLUrfzclR1A= 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /cert/server-cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDFDCCAfwCAQEwDQYJKoZIhvcNAQEFBQAwUDELMAkGA1UEBhMCVFIxDjAMBgNV 3 | BAgMBUl6bWlyMQ4wDAYDVQQHDAVJem1pcjEhMB8GA1UECgwYSW50ZXJuZXQgV2lk 4 | Z2l0cyBQdHkgTHRkMB4XDTE2MDkwNjA1NTIxN1oXDTE4MDkwNjA1NTIxN1owUDEL 5 | MAkGA1UEBhMCVFIxDjAMBgNVBAgMBUl6bWlyMQ4wDAYDVQQHDAVJem1pcjEhMB8G 6 | A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEF 7 | AAOCAQ8AMIIBCgKCAQEA7bmiPnz6cg8KEdbhLJF/vWF3DBe+51hruxPn5gMBhdwv 8 | Id1HzdOjTEo+onXzeEARGX1dMKcXuHTE4MxujdS6cOxugqZXhJjw9EUNDHv9LS6I 9 | bn/jpTrrFhebX8WHro5rYsbVp43nzxrWfx0V6IahBfQneubpXQaLJVhOFh0fNeT/ 10 | RP4K7RdeCGt/qojK9VminXODtk0mc83BYxFgjMYuRjSrEa1o8JXThXF2dSUwQC/r 11 | 2WhxBaEFwFpdvFE6SRRYmLK2XLfybAjX+bDFDG+ZYANbO3xpvepNcFEE5Pf1AsW0 12 | fULYACPElUoKxp/jm5uazDltO8cHmqP25UR3YHfJiwIDAQABMA0GCSqGSIb3DQEB 13 | BQUAA4IBAQCx2rhYDGb5d5xq8XQCAlWqNnIvtCBlXEr82lFS0K/V7cblvpyX3Sk0 14 | ADg+098rPNPDEbe358dj3N19LL5fkK808tbrJnOCFNA6mcvwV7PvERroLwZNaVGV 15 | 7JNhirc7UmA8f9OPTZWB7LXvogQUooQZoSOQn1Ku0TJlrTC6EyHduOJzPp7OV5hU 16 | aHs7SU+O4yPZsRuWgVEkYHceKBtbyjIMesO1sqqBPOQLxGam/VFVHBQxudjZr3X5 17 | WYbw9Z5i2/VvN1srrmzyjBRf6fvcAoRXTZG0EtadnoytJW572GNYQXmWL9H6PXm1 18 | wg7yRsSFVdNO7v2uL7Cf72hu7VJ1ZJf+ 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /cert/server-key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEA7bmiPnz6cg8KEdbhLJF/vWF3DBe+51hruxPn5gMBhdwvId1H 3 | zdOjTEo+onXzeEARGX1dMKcXuHTE4MxujdS6cOxugqZXhJjw9EUNDHv9LS6Ibn/j 4 | pTrrFhebX8WHro5rYsbVp43nzxrWfx0V6IahBfQneubpXQaLJVhOFh0fNeT/RP4K 5 | 7RdeCGt/qojK9VminXODtk0mc83BYxFgjMYuRjSrEa1o8JXThXF2dSUwQC/r2Whx 6 | BaEFwFpdvFE6SRRYmLK2XLfybAjX+bDFDG+ZYANbO3xpvepNcFEE5Pf1AsW0fULY 7 | ACPElUoKxp/jm5uazDltO8cHmqP25UR3YHfJiwIDAQABAoIBABsCWlKrNHE9EDHS 8 | ria4KUFFD5eKIyB5xVOuAUz6znN8/sXkNEJIZ2dFkDQ5Hn5tGQRCYBIlCeblLXE1 9 | COKIZt5pnmytpYflMIswRTlD5RH7FXkogp7FQOxzpi2NbcsBV2YTnTa6QrofHHu4 10 | vEJ8+VKJ2Op3zc/J7IlDrSuKB3/B2sIxFSnOHarFj+Y9562LPkGL6u08Dd9KPQ4S 11 | 1GqHISamvNvi9dKAqV4tcTunp3+Jtw+LWeFelgWQoftgeQJjCNkM6XPEHMvsyVYb 12 | FlmCtJmbrOuTn1zY39inwVavUwbeGgp698PN0iDowhgnfOSXYeSSwd99TFjEnq+h 13 | zWhbd+ECgYEA/fwxkIk6EIQ0z77tQBMOE6E0co5/lshPnjVWzzBfkFTyQMR7Vo7c 14 | OJQRKM9lXA8i3gbUnEOXLaDp2FlLM7OkCssNoRS3vs+ws/k5AvucNzz5yR2v9XKi 15 | xc/WolWRK1XnI3peV61blfmQNLW4Iu/BiMostSoHyMMO3OlkRKVmY6kCgYEA75xr 16 | IjT+ZTVwbIpinEy06I4D6mcq5tBGxbh+dAWSBER3COnXUPkwxhiuF9aKJ7I2bZB7 17 | typnGykLLopW8ez4jBk451M5UsItdwkMZ8LQSQct7XUtGdswih5TZbVENeZQv5yb 18 | 8v3Ogq3Hn3AV/kdzQT+05RmLrgcQRcbGYHOpxBMCgYBXeATxy/CtQ3qUmSJH0Mgm 19 | Rnwzf5O9L9sLlz8AN56RrE+JsqDvXF/HNGb8cQBtReEyKZQLBNQJqMMf+RCHC7TQ 20 | 6sEU2ne1RP2L3aaeulxAQcWcV5cDpqkcCsnWOjW2gO0LMNdQxqkl6z6YiISTs5Ip 21 | M8dV1NW9dD+oINChO30ZkQKBgQDTdIrnSTUoRBzO7OFRZjBQ+uNhvWITB8MtQj/2 22 | S+Qsd+9EonkuMHtmluksiFyr44486sEDSBxtz67ah6NrJOHDYY7iNoPkERDmd8nG 23 | 2BnQGNHjOQFwTAtuGI0ouOtVVcf1EVRfALV6hbohbl06ZfvEnWAavoySLBGkQmNX 24 | QzuSmQKBgARCfXH+hffyeRK+hYV8SJe7vL1TAJrmCAzWvW3fodn+/wImy/3ySZkh 25 | Itg/6XawUOTf54Ltl0Z3kTAGDmxDYlzHUenxT7qdNzsNrBsW7Lqqw+oKShGzSqd2 26 | YuCxrHxUCNibNmQJTwBAruW7dcfECZIA7b+weqH+tuAijFkL8b1R 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /conf/dbshield.yml: -------------------------------------------------------------------------------- 1 | ############################## 2 | ### General ### 3 | ############################# 4 | 5 | # Operating mode 6 | # possible values: 7 | # learning - add queries to training set (default) 8 | # protect - take action against abnormal queries 9 | mode: learning 10 | 11 | # DBMS type(default: mysql) 12 | dbms: mysql 13 | 14 | # IP address to listen on(default: 0.0.0.0) 15 | listenIP: 0.0.0.0 16 | 17 | # Port number to listen on (0 means dbms default port ex. 3306 for mysql) 18 | listenPort: 5000 19 | 20 | # IP address of the server 21 | targetIP: 127.0.0.1 22 | 23 | # Port number of the dbms (0 means dbms default port ex. 3306 for mysql) 24 | targetPort: 0 25 | 26 | # TLS 27 | tlsPrivateKey: cert/server-key.pem 28 | tlsCertificate: cert/server-cert.pem 29 | 30 | # Directory to save internal database(default: in model directory under OS's temp directory) 31 | dbDir: "/tmp/model/" 32 | 33 | # Connection timeout 34 | # Valid units are "ns", "us" (or "µs"), "ms", "s", "m" and "h". (default: 5s) 35 | timeout: 5s 36 | 37 | # Databasse synchronisation interval, 0 will force sync after each commit. 38 | # Valid units are "ns", "us" (or "µs"), "ms", "s", "m" and "h". (default: 5s) 39 | SyncInterval: 5s 40 | 41 | ############################# 42 | ### HTTP Service ### 43 | ############################# 44 | # Run http interface(default: yes) 45 | http: yes 46 | 47 | # Serve https (default: yes) 48 | httpSSL: yes 49 | 50 | # IP address to listen on(default: 127.0.0.1) 51 | httpIP: 127.0.0.1 52 | 53 | # Port number to listen on(default: 8070) 54 | httpPort: 8070 55 | 56 | # Password 57 | httpPassword: ChangeMe 58 | 59 | ############################# 60 | ### Protect ### 61 | ############################# 62 | 63 | # Action to take against abnormal requests 64 | # possible values: 65 | # drop - close the connection (default) 66 | # pass - pass the query to server 67 | action: drop 68 | 69 | # Properties to check for abnormality 70 | # 71 | # possible values: (you can choose more than one - comma separated) 72 | # user - username 73 | # source - request sourece address 74 | # 75 | # Note: leave empty to avoid adiditional checks 76 | additionalChecks: user,source 77 | 78 | ############################# 79 | ### Logging ### 80 | ############################# 81 | 82 | # Log depth 83 | # 1 warning 84 | # 2 info 85 | # 4 debug 86 | # default: 3 (warning + info) 87 | logLevel: 3 88 | 89 | # Log path 90 | # possible values: 91 | # /path/to/file 92 | # stdout 93 | # stderr (default) 94 | logPath: stderr 95 | -------------------------------------------------------------------------------- /dbshield/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "regexp" 8 | "strings" 9 | "time" 10 | 11 | "github.com/nim4/DBShield/dbshield/logger" 12 | "github.com/nim4/DBShield/dbshield/utils" 13 | "github.com/spf13/viper" 14 | ) 15 | 16 | type mask struct { 17 | MatchExp *regexp.Regexp 18 | ReplaceExp []byte 19 | PaddingCharacter []byte 20 | } 21 | 22 | //Configurations structure to hold user configurations 23 | type Configurations struct { 24 | Learning bool 25 | CheckUser bool 26 | CheckSource bool 27 | 28 | LogLevel uint 29 | LogPath string 30 | 31 | DBType string 32 | DB uint `json:"-"` 33 | 34 | DBDir string 35 | 36 | ListenIP string 37 | ListenPort uint 38 | 39 | TargetIP string 40 | TargetPort uint 41 | 42 | TLSPrivateKey string 43 | TLSCertificate string 44 | 45 | HTTP bool 46 | HTTPSSL bool 47 | HTTPAddr string 48 | HTTPPassword string 49 | 50 | Action string 51 | ActionFunc func() error `json:"-"` 52 | 53 | Timeout time.Duration 54 | 55 | SyncInterval time.Duration 56 | //Key-> database.table.column 57 | //Masks map[string]mask 58 | } 59 | 60 | //Config holds current configs 61 | var Config Configurations 62 | 63 | func strConfig(key string) (ret string, err error) { 64 | if viper.IsSet(key) { 65 | ret = viper.GetString(key) 66 | return 67 | } 68 | err = fmt.Errorf("Invalid '%s' cofiguration", key) 69 | return 70 | } 71 | 72 | func strConfigDefualt(key, defaultValue string) (ret string) { 73 | if viper.IsSet(key) { 74 | ret = viper.GetString(key) 75 | return 76 | } 77 | logger.Infof("'%s' not configured, assuming: %s", key, defaultValue) 78 | ret = defaultValue 79 | return 80 | } 81 | 82 | func intConfig(key string, defaultValue, min uint) (ret uint, err error) { 83 | if viper.IsSet(key) { 84 | tmp := viper.GetInt(key) 85 | if tmp < 0 { 86 | err = fmt.Errorf("Invalid '%s' cofiguration: %v\n", key, tmp) 87 | return 88 | } 89 | ret = uint(tmp) 90 | if ret < min { 91 | err = fmt.Errorf("Invalid '%s' cofiguration: %v\n", key, ret) 92 | return 93 | } 94 | return 95 | } 96 | logger.Infof("'%s' not configured, assuming: %s", key, defaultValue) 97 | ret = defaultValue 98 | return 99 | } 100 | 101 | func configGeneral() (err error) { 102 | if viper.IsSet("mode") { 103 | switch viper.GetString("mode") { 104 | case "protect": 105 | Config.Learning = false 106 | case "learning": 107 | Config.Learning = true 108 | default: 109 | return errors.New("Invalid 'mode' cofiguration: " + viper.GetString("mode")) 110 | } 111 | } else { 112 | logger.Infof("'mode' not configured, assuming: learning") 113 | Config.Learning = true 114 | } 115 | 116 | Config.ListenPort, err = intConfig("listenPort", 0, 0) 117 | if err != nil { 118 | return err 119 | } 120 | 121 | Config.TargetPort, err = intConfig("targetPort", 0, 0) 122 | if err != nil { 123 | return err 124 | } 125 | Config.TargetIP, err = strConfig("targetIP") 126 | if err != nil { 127 | return err 128 | } 129 | 130 | //String values 131 | Config.TLSPrivateKey, err = strConfig("tlsPrivateKey") 132 | if err != nil { 133 | return err 134 | } 135 | 136 | Config.TLSCertificate, err = strConfig("tlsCertificate") 137 | if err != nil { 138 | return err 139 | } 140 | 141 | Config.DBDir = strConfigDefualt("dbDir", os.TempDir()+"/model") 142 | err = os.MkdirAll(Config.DBDir, 0740) //Make dbDir, just in case its not there 143 | if err != nil { 144 | return err 145 | } 146 | 147 | Config.DBType = strConfigDefualt("dbms", "mysql") 148 | 149 | Config.ListenIP = strConfigDefualt("listenIP", "0.0.0.0") 150 | 151 | if timeout := viper.GetString("timeout"); timeout != "" { 152 | Config.Timeout, err = time.ParseDuration(timeout) 153 | if err != nil { 154 | return err 155 | } 156 | } else { 157 | Config.Timeout = 5 * time.Second 158 | } 159 | 160 | if syn := viper.GetString("syncInterval"); syn != "" { 161 | Config.SyncInterval, err = time.ParseDuration(syn) 162 | if err != nil { 163 | return err 164 | } 165 | } else { 166 | Config.SyncInterval = 5 * time.Second 167 | } 168 | return nil 169 | } 170 | 171 | func configProtect() error { 172 | if viper.IsSet("action") { 173 | Config.Action = viper.GetString("action") 174 | switch Config.Action { 175 | case "drop": //Close the connection 176 | Config.ActionFunc = utils.ActionDrop 177 | case "pass": //Pass the query to server 178 | Config.ActionFunc = nil 179 | default: 180 | return errors.New("Invalid 'action' cofiguration: " + Config.Action) 181 | } 182 | } else { 183 | logger.Infof("'action' not configured, assuming: drop") 184 | Config.ActionFunc = utils.ActionDrop 185 | } 186 | 187 | if viper.IsSet("additionalChecks") { 188 | for _, check := range strings.Split(viper.GetString("additionalChecks"), ",") { 189 | switch check { 190 | case "user": 191 | Config.CheckUser = true 192 | case "source": 193 | Config.CheckSource = true 194 | default: 195 | return errors.New("Invalid 'additionalChecks' cofiguration: " + check) 196 | } 197 | } 198 | } 199 | return nil 200 | } 201 | 202 | func configLog() error { 203 | var err error 204 | Config.LogPath = strConfigDefualt("logPath", "stderr") 205 | Config.LogLevel, err = intConfig("logLevel", 3, 0) 206 | return err 207 | } 208 | 209 | func configHTTP() error { 210 | Config.HTTP = viper.GetBool("http") 211 | if Config.HTTP { 212 | Config.HTTPPassword = viper.GetString("httpPassword") 213 | httpIP := strConfigDefualt("httpIP", "127.0.0.1") 214 | httpPort, err := intConfig("httpPort", 8070, 1) 215 | if err != nil { 216 | return err 217 | } 218 | Config.HTTPSSL = viper.GetBool("httpSSL") 219 | Config.HTTPAddr = fmt.Sprintf("%s:%d", httpIP, httpPort) 220 | } 221 | return nil 222 | } 223 | 224 | //ParseConfig and return error if its not valid 225 | func ParseConfig(configFile string) error { 226 | Config = Configurations{} // Reset configs 227 | viper.SetConfigFile(configFile) 228 | err := viper.ReadInConfig() // Read the config file 229 | if err != nil { 230 | return fmt.Errorf("Fatal error - config file: %s \n", err) 231 | } 232 | err = configGeneral() 233 | if err != nil { 234 | return err 235 | } 236 | 237 | if !Config.Learning { 238 | err = configProtect() 239 | if err != nil { 240 | return err 241 | } 242 | } 243 | 244 | err = configLog() 245 | if err != nil { 246 | return err 247 | } 248 | 249 | err = configHTTP() 250 | if err != nil { 251 | return err 252 | } 253 | return nil 254 | } 255 | -------------------------------------------------------------------------------- /dbshield/config/config_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/spf13/viper" 8 | ) 9 | 10 | func TestParseConfig(t *testing.T) { 11 | err := ParseConfig("../../conf/dbshield.yml") 12 | if err != nil { 13 | t.Error("Got error", err) 14 | } 15 | 16 | err = ParseConfig("Invalid.yml") 17 | if err == nil { 18 | t.Error("Expected error") 19 | } 20 | 21 | viper.Set("mode", "Invalid") //make configGeneral fail 22 | err = ParseConfig("../../conf/dbshield.yml") 23 | if err == nil { 24 | t.Error("Expected error") 25 | } 26 | 27 | viper.Set("mode", "protect") //make configProtect fail 28 | viper.Set("action", "xyz") 29 | err = ParseConfig("../../conf/dbshield.yml") 30 | if err == nil { 31 | t.Error("Expected error") 32 | } 33 | viper.Set("action", "drop") 34 | 35 | viper.Set("logLevel", -1) //make configLog fail 36 | err = ParseConfig("../../conf/dbshield.yml") 37 | if err == nil { 38 | t.Error("Expected error") 39 | } 40 | viper.Set("logLevel", 0) 41 | 42 | viper.Set("http", true) //make configHTTP fail 43 | viper.Set("httpPort", -1) 44 | err = ParseConfig("../../conf/dbshield.yml") 45 | if err == nil { 46 | t.Error("Expected error") 47 | } 48 | viper.Set("logLevel", 0) 49 | } 50 | 51 | //Check the cases which are not in default config 52 | 53 | func TestStrConfigDefualt(t *testing.T) { 54 | ret := strConfigDefualt("Invalid", "X") 55 | if ret != "X" { 56 | t.Error("Expected X got", ret) 57 | } 58 | } 59 | 60 | func TestIntConfig(t *testing.T) { 61 | ret, err := intConfig("Invalid", 1, 1) 62 | if err != nil || ret != 1 { 63 | t.Error("Expected 1 or error got", ret, err) 64 | } 65 | } 66 | 67 | func TestConfigGeneral(t *testing.T) { 68 | viper.Reset() 69 | viper.Set("targetIP", "127.0.0.1") 70 | viper.Set("tlsPrivateKey", "key") 71 | viper.Set("tlsCertificate", "cert") 72 | 73 | viper.Set("mode", "protect") 74 | err := configGeneral() 75 | if err != nil { 76 | t.Error("Got error", err) 77 | } 78 | 79 | viper.Set("mode", "Invalid") 80 | err = configGeneral() 81 | if err == nil { 82 | t.Error("Expected error") 83 | } 84 | 85 | viper.Set("mode", nil) 86 | err = configGeneral() 87 | if err != nil { 88 | t.Error("Got error", err) 89 | } 90 | viper.Set("mode", "learning") 91 | 92 | viper.Set("listenPort", -1) 93 | err = configGeneral() 94 | if err == nil { 95 | t.Error("Expected error") 96 | } 97 | viper.Set("listenPort", 0) 98 | 99 | viper.Set("targetPort", -1) 100 | err = configGeneral() 101 | if err == nil { 102 | t.Error("Expected error") 103 | } 104 | viper.Set("targetPort", 0) 105 | 106 | viper.Set("timeout", "X") 107 | err = configGeneral() 108 | if err == nil { 109 | t.Error("Expected error") 110 | } 111 | viper.Set("timeout", "5s") 112 | 113 | viper.Set("syncInterval", "X") 114 | err = configGeneral() 115 | if err == nil { 116 | t.Error("Expected error") 117 | } 118 | viper.Set("syncInterval", "5s") 119 | 120 | viper.Reset() 121 | err = configGeneral() 122 | if err == nil { 123 | t.Error("Expected error") 124 | } 125 | viper.Set("targetIP", "127.0.0.1") 126 | 127 | viper.Reset() 128 | viper.Set("targetIP", "127.0.0.1") 129 | viper.Set("tlsPrivateKey", "key") 130 | err = configGeneral() 131 | if err == nil { 132 | t.Error("Expected error") 133 | } 134 | 135 | viper.Reset() 136 | viper.Set("targetIP", "127.0.0.1") 137 | viper.Set("tlsCertificate", "cert") 138 | err = configGeneral() 139 | if err == nil { 140 | t.Error("Expected error") 141 | } 142 | viper.Set("tlsPrivateKey", "key") 143 | 144 | // Can't make directory named after file. 145 | fpath := os.TempDir() + "/file" 146 | f, err := os.Create(fpath) 147 | if err != nil { 148 | t.Fatalf("create %q: %s", fpath, err) 149 | } 150 | f.Close() 151 | 152 | viper.Set("dbDir", fpath) 153 | err = configGeneral() 154 | if err == nil { 155 | t.Error("Expected error") 156 | } 157 | } 158 | 159 | func TestConfigProtect(t *testing.T) { 160 | viper.Set("action", "drop") 161 | err := configProtect() 162 | if err != nil { 163 | t.Error("Got error", err) 164 | } 165 | 166 | viper.Set("action", "pass") 167 | err = configProtect() 168 | if err != nil { 169 | t.Error("Got error", err) 170 | } 171 | 172 | viper.Set("action", "Invalid") 173 | err = configProtect() 174 | if err == nil { 175 | t.Error("Expected error") 176 | } 177 | 178 | viper.Set("action", nil) 179 | err = configProtect() 180 | if err != nil { 181 | t.Error("Got error", err) 182 | } 183 | 184 | viper.Set("additionalChecks", "Invalid") 185 | err = configProtect() 186 | if err == nil { 187 | t.Error("Expected error") 188 | } 189 | } 190 | 191 | func TestConfigHTTP(t *testing.T) { 192 | viper.Set("http", true) 193 | viper.Set("httpPort", "Invalid") 194 | err := configHTTP() 195 | if err == nil { 196 | t.Error("Expected error") 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /dbshield/dbms/db2.go: -------------------------------------------------------------------------------- 1 | package dbms 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "io" 7 | "net" 8 | "time" 9 | 10 | "github.com/nim4/DBShield/dbshield/logger" 11 | "github.com/nim4/DBShield/dbshield/sql" 12 | ) 13 | 14 | //DB2 DBMS 15 | type DB2 struct { 16 | client net.Conn 17 | server net.Conn 18 | certificate tls.Certificate 19 | currentDB []byte 20 | username []byte 21 | reader func(io.Reader) ([]byte, error) 22 | } 23 | 24 | //SetCertificate to use if client asks for SSL 25 | func (d *DB2) SetCertificate(crt, key string) (err error) { 26 | d.certificate, err = tls.LoadX509KeyPair(crt, key) 27 | return 28 | } 29 | 30 | //SetReader function for sockets IO 31 | func (d *DB2) SetReader(f func(io.Reader) ([]byte, error)) { 32 | d.reader = f 33 | } 34 | 35 | //SetSockets for dbms (client and server sockets) 36 | func (d *DB2) SetSockets(c, s net.Conn) { 37 | defer handlePanic() 38 | d.client = c 39 | d.server = s 40 | } 41 | 42 | //Close sockets 43 | func (d *DB2) Close() { 44 | defer handlePanic() 45 | d.client.Close() 46 | d.server.Close() 47 | } 48 | 49 | //DefaultPort of the DBMS 50 | func (d *DB2) DefaultPort() uint { 51 | return 50000 52 | } 53 | 54 | //Handler gets incoming requests 55 | func (d *DB2) Handler() (err error) { 56 | defer handlePanic() 57 | defer d.Close() 58 | 59 | success, err := d.handleLogin() 60 | if err != nil { 61 | return 62 | } 63 | if !success { 64 | logger.Warning("Login failed") 65 | return 66 | } 67 | end := false 68 | for { 69 | var buf []byte 70 | //Read client request 71 | buf, err = d.reader(d.client) 72 | if err != nil { 73 | return 74 | } 75 | for len(buf) > 0 { 76 | dr, n := parseDRDA(buf) 77 | buf = buf[n:] 78 | if dr.ddm[0] == 0xc0 && dr.ddm[1] == 0x04 { 79 | //ENDUOWRM (end) 80 | end = true 81 | } else if dr.ddm[0] == 0x24 && dr.ddm[1] == 0x14 { 82 | context := sql.QueryContext{ 83 | Query: dr.param, 84 | Database: d.currentDB, 85 | User: d.username, 86 | Client: remoteAddrToIP(d.client.RemoteAddr()), 87 | Time: time.Now(), 88 | } 89 | processContext(context) 90 | } 91 | } 92 | 93 | //Send request to server 94 | _, err = d.server.Write(buf) 95 | if err != nil { 96 | return 97 | } 98 | 99 | if end { 100 | return 101 | } 102 | 103 | err = readWrite(d.server, d.client, d.reader) 104 | if err != nil { 105 | return 106 | } 107 | } 108 | } 109 | 110 | func (d *DB2) handleLogin() (success bool, err error) { 111 | //Receive EXCSAT | ACCSEC 112 | err = readWrite(d.client, d.server, d.reader) 113 | if err != nil { 114 | return 115 | } 116 | 117 | //Receive EXCSATRD | ACCSECRD 118 | err = readWrite(d.server, d.client, d.reader) 119 | if err != nil { 120 | return 121 | } 122 | 123 | //Receive Auth 124 | buf, err := d.reader(d.client) 125 | if err != nil { 126 | return 127 | } 128 | //Send result 129 | _, err = d.server.Write(buf) 130 | if err != nil { 131 | return 132 | } 133 | 134 | //Get database name 135 | startIndex := bytes.Index(buf, []byte{0x21, 0x10}) 136 | buf = buf[startIndex+2:] 137 | atByteIndex := bytes.IndexByte(buf, 0x40) // 0x40 = @ 138 | d.currentDB = ebc2asc(buf[:atByteIndex]) 139 | //Get username 140 | startIndex = bytes.Index(buf, []byte{0x11, 0xa0}) 141 | buf = buf[startIndex+2:] 142 | nullByteIndex := bytes.IndexByte(buf, 0x00) 143 | d.username = ebc2asc(buf[:nullByteIndex]) 144 | //Receive result 145 | buf, err = d.reader(d.server) 146 | if err != nil { 147 | return 148 | } 149 | //Send result 150 | _, err = d.client.Write(buf) 151 | if err != nil { 152 | return 153 | } 154 | 155 | for { 156 | dr, n := parseDRDA(buf) 157 | success = len(dr.ddm) == 2 && dr.ddm[0] == 0x22 && dr.ddm[1] == 0x01 158 | buf = buf[n:] 159 | if success || len(buf) == 0 { 160 | break 161 | } 162 | } 163 | 164 | return 165 | } 166 | 167 | type drda struct { 168 | ddm []byte 169 | param []byte 170 | } 171 | 172 | func parseDRDA(buf []byte) (dr drda, n int) { 173 | n = int(buf[0])*256 + int(buf[1]) 174 | dr.ddm = buf[8:10] 175 | if len(buf) > 15 { 176 | nullByteIndex := bytes.IndexByte(buf[15:], 0xff) // 0xff is string terminator 177 | if nullByteIndex > 0 { 178 | dr.param = buf[15 : nullByteIndex+15] 179 | } 180 | } 181 | return 182 | } 183 | -------------------------------------------------------------------------------- /dbshield/dbms/db2_test.go: -------------------------------------------------------------------------------- 1 | package dbms_test 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "testing" 7 | 8 | "github.com/nim4/DBShield/dbshield/dbms" 9 | "github.com/nim4/mock" 10 | ) 11 | 12 | var sampleDB2 = [...][]byte{ 13 | { 14 | 0x00, 0xd0, 0xd0, 0x41, 0x00, 0x01, 0x00, 0xca, 0x10, 0x41, 0x00, 0x82, 15 | 0x11, 0x5e, 0x97, 0xa8, 0xa3, 0x88, 0x96, 0x95, 0x40, 0x40, 0x40, 0x40, 16 | 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0xf0, 0xf0, 17 | 0xf0, 0xf0, 0xf7, 0xf8, 0xf8, 0xf1, 0xf0, 0xf0, 0xf0, 0x00, 0x00, 0x00, 18 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 19 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 20 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xf0, 0xf0, 0xf0, 0xf1, 0xa4, 0xa2, 21 | 0x85, 0x99, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 22 | 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 23 | 0x40, 0x40, 0x40, 0x40, 0xe2, 0xc1, 0xd4, 0xd7, 0xd3, 0xc5, 0x40, 0x40, 24 | 0xf0, 0xc4, 0xc2, 0xf2, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 25 | 0x40, 0x40, 0x40, 0x40, 0x40, 0xf0, 0xf0, 0xf1, 0x00, 0x1c, 0x14, 0x04, 26 | 0x14, 0x03, 0x00, 0x0a, 0x24, 0x07, 0x00, 0x0b, 0x14, 0x74, 0x00, 0x05, 27 | 0x24, 0x0f, 0x00, 0x08, 0x14, 0x40, 0x00, 0x09, 0x1c, 0x08, 0x04, 0xb8, 28 | 0x00, 0x13, 0x11, 0x47, 0xd8, 0xc4, 0xc2, 0xf2, 0x61, 0xd3, 0xc9, 0xd5, 29 | 0xe4, 0xe7, 0xe7, 0xf8, 0xf6, 0xf6, 0xf4, 0x00, 0x09, 0x11, 0x6d, 0xd5, 30 | 0x96, 0x92, 0x89, 0x81, 0x00, 0x0c, 0x11, 0x5a, 0xe2, 0xd8, 0xd3, 0xf1, 31 | 0xf0, 0xf0, 0xf5, 0xf5, 0x00, 0x4a, 0xd0, 0x01, 0x00, 0x02, 0x00, 0x44, 32 | 0x10, 0x6d, 0x00, 0x06, 0x11, 0xa2, 0x00, 0x09, 0x00, 0x16, 0x21, 0x10, 33 | 0xe2, 0xc1, 0xd4, 0xd7, 0xd3, 0xc5, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 34 | 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x24, 0x11, 0xdc, 0x23, 0xc4, 35 | 0xde, 0x5b, 0xff, 0xf1, 0x70, 0xa2, 0xdf, 0xa8, 0x4c, 0x89, 0x2d, 0xf0, 36 | 0xd7, 0xa3, 0x8b, 0x8a, 0x1c, 0x9b, 0x62, 0x33, 0x97, 0x55, 0xc6, 0xba, 37 | 0x9b, 0xcb, 0x01, 0x4d, 0x7e, 0xf3, 38 | }, //Client 39 | { 40 | 0x00, 0x86, 0xd0, 0x43, 0x00, 0x01, 0x00, 0x80, 0x14, 0x43, 0x00, 0x35, 41 | 0x11, 0x5e, 0x84, 0x82, 0xf2, 0x89, 0x95, 0xa2, 0xa3, 0xf1, 0x84, 0x82, 42 | 0xf2, 0x81, 0x87, 0x85, 0x95, 0xa3, 0xf0, 0xf0, 0xf0, 0xf0, 0xf2, 0xf4, 43 | 0xc6, 0xc1, 0x6c, 0xc6, 0xc5, 0xc4, 0x6c, 0xe8, 0xf0, 0xf0, 0x40, 0x40, 44 | 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 45 | 0x40, 0x40, 0xf1, 0x00, 0x1c, 0x14, 0x04, 0x14, 0x03, 0x00, 0x0a, 0x24, 46 | 0x07, 0x00, 0x0a, 0x14, 0x74, 0x00, 0x05, 0x24, 0x0f, 0x00, 0x08, 0x14, 47 | 0x40, 0x00, 0x09, 0x1c, 0x08, 0x00, 0x00, 0x00, 0x13, 0x11, 0x47, 0xd8, 48 | 0xc4, 0xc2, 0xf2, 0x61, 0xd3, 0xc9, 0xd5, 0xe4, 0xe7, 0xe7, 0xf8, 0xf6, 49 | 0xf6, 0xf4, 0x00, 0x0c, 0x11, 0x6d, 0x84, 0x82, 0xf2, 0x89, 0x95, 0xa2, 50 | 0xa3, 0xf1, 0x00, 0x0c, 0x11, 0x5a, 0xe2, 0xd8, 0xd3, 0xf1, 0xf0, 0xf0, 51 | 0xf5, 0xf5, 0x00, 0x17, 0xd0, 0x03, 0x00, 0x02, 0x00, 0x11, 0x14, 0xac, 52 | 0x00, 0x08, 0x11, 0xa2, 0x00, 0x03, 0x00, 0x05, 0x00, 0x05, 0x11, 0xa4, 53 | 0x01, 54 | }, 55 | { 56 | 0x00, 0x26, 0xd0, 0x41, 0x00, 0x01, 0x00, 0x20, 0x10, 0x6d, 0x00, 0x06, 57 | 0x11, 0xa2, 0x00, 0x03, 0x00, 0x16, 0x21, 0x10, 0xe2, 0xc1, 0xd4, 0xd7, 58 | 0xd3, 0xc5, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 59 | 0x40, 0x40, 0x00, 0x3e, 0xd0, 0x41, 0x00, 0x02, 0x00, 0x38, 0x10, 0x6e, 60 | 0x00, 0x06, 0x11, 0xa2, 0x00, 0x03, 0x00, 0x16, 0x21, 0x10, 0xe2, 0xc1, 61 | 0xd4, 0xd7, 0xd3, 0xc5, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 62 | 0x40, 0x40, 0x40, 0x40, 0x00, 0x0c, 0x11, 0xa1, 0x84, 0x82, 0xf2, 0x89, 63 | 0x95, 0xa2, 0xa3, 0xf1, 0x00, 0x0c, 0x11, 0xa0, 0x84, 0x82, 0xf2, 0x89, 64 | 0x95, 0xa2, 0xa3, 0xf1, 0x00, 0xbc, 0xd0, 0x01, 0x00, 0x03, 0x00, 0xb6, 65 | 0x20, 0x01, 0x00, 0x06, 0x21, 0x0f, 0x24, 0x07, 0x00, 0x20, 0x21, 0x35, 66 | 0xf1, 0xf2, 0xf7, 0x4b, 0xf0, 0x4b, 0xf0, 0x4b, 0xf1, 0x4b, 0xf3, 0xf6, 67 | 0xf3, 0xf9, 0xf4, 0x4b, 0xf1, 0xf6, 0xf1, 0xf0, 0xf2, 0xf2, 0xf0, 0xf9, 68 | 0xf0, 0xf8, 0xf2, 0xf8, 0x00, 0x16, 0x21, 0x10, 0xe2, 0xc1, 0xd4, 0xd7, 69 | 0xd3, 0xc5, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 70 | 0x40, 0x40, 0x00, 0x0c, 0x11, 0x2e, 0xe2, 0xd8, 0xd3, 0xf1, 0xf0, 0xf0, 71 | 0xf5, 0xf5, 0x00, 0x0d, 0x00, 0x2f, 0xd8, 0xe3, 0xc4, 0xe2, 0xd8, 0xd3, 72 | 0xe7, 0xf8, 0xf6, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x06, 0x11, 0x9c, 0x04, 73 | 0xb8, 0x00, 0x06, 0x11, 0x9d, 0x04, 0xb0, 0x00, 0x06, 0x11, 0x9e, 0x04, 74 | 0xb8, 0x00, 0x06, 0x19, 0x13, 0x04, 0xb8, 0x00, 0x3c, 0x21, 0x04, 0x37, 75 | 0xe2, 0xd8, 0xd3, 0xf1, 0xf0, 0xf0, 0xf5, 0xf5, 0xd3, 0x89, 0x95, 0xa4, 76 | 0xa7, 0x61, 0xe7, 0xf8, 0xf6, 0xf6, 0xf4, 0x40, 0x40, 0x40, 0x40, 0x40, 77 | 0x40, 0x40, 0x97, 0xa8, 0xa3, 0x88, 0x96, 0x95, 0x40, 0x40, 0x40, 0x40, 78 | 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x84, 0x82, 79 | 0xf2, 0x89, 0x95, 0xa2, 0xa3, 0xf1, 0x00, 0x00, 0x05, 0x21, 0x3b, 0xf1, 80 | }, 81 | { 82 | 0x00, 0x10, 0xd0, 0x43, 0x00, 0x01, 0x00, 0x0a, 0x14, 0xac, 0x00, 0x06, 83 | 0x11, 0xa2, 0x00, 0x03, 0x00, 0x15, 0xd0, 0x42, 0x00, 0x02, 0x00, 0x0f, 84 | 0x12, 0x19, 0x00, 0x06, 0x11, 0x49, 0x00, 0x00, 0x00, 0x05, 0x11, 0xa4, 85 | 0x00, 0x00, 0x5d, 0xd0, 0x52, 0x00, 0x03, 0x00, 0x57, 0x22, 0x01, 0x00, 86 | 0x06, 0x11, 0x49, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x2e, 0xe2, 0xd8, 0xd3, 87 | 0xf1, 0xf0, 0xf0, 0xf5, 0xf5, 0x00, 0x0d, 0x00, 0x2f, 0xd8, 0xe3, 0xc4, 88 | 0xe2, 0xd8, 0xd3, 0xe7, 0xf8, 0xf6, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x06, 89 | 0x11, 0x9c, 0x04, 0xb8, 0x00, 0x06, 0x11, 0x9d, 0x04, 0xb0, 0x00, 0x06, 90 | 0x11, 0x9e, 0x04, 0xb8, 0x00, 0x06, 0x19, 0x13, 0x04, 0xb8, 0x00, 0x06, 91 | 0x21, 0x03, 0x01, 0x8c, 0x00, 0x06, 0x21, 0x25, 0x24, 0x35, 0x00, 0x0c, 92 | 0x11, 0xa0, 0xc4, 0xc2, 0xf2, 0xc9, 0xd5, 0xe2, 0xe3, 0xf1, 0x00, 0x91, 93 | 0xd0, 0x03, 0x00, 0x03, 0x00, 0x8b, 0x24, 0x08, 0x00, 0x00, 0x00, 0x00, 94 | 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x53, 0x51, 0x4c, 0x31, 0x30, 0x30, 95 | 0x35, 0x35, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 96 | 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 97 | 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 98 | 0x20, 0x20, 0x00, 0x12, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x20, 0x20, 99 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x38, 100 | 0x31, 0xff, 0x31, 0x32, 0x30, 0x38, 0xff, 0x44, 0x42, 0x32, 0x49, 0x4e, 101 | 0x53, 0x54, 0x31, 0xff, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0xff, 0x51, 102 | 0x44, 0x42, 0x32, 0x2f, 0x4c, 0x49, 0x4e, 0x55, 0x58, 0x58, 0x38, 0x36, 103 | 0x36, 0x34, 0xff, 0x33, 0x39, 0x36, 0xff, 0x33, 0x39, 0x36, 0xff, 0x30, 104 | 0xff, 0x31, 0x32, 0x30, 0x38, 0xff, 0x30, 0xff, 0x00, 0x00, 0xff, 105 | }, 106 | { 107 | 0x00, 0x12, 0xd0, 0x41, 0x00, 0x01, 0x00, 0x0c, 0x10, 0x41, 0x00, 0x08, 108 | 0x14, 0x04, 0x14, 0xcc, 0x04, 0xb8, 0x00, 0x4e, 0xd0, 0x51, 0x00, 0x02, 109 | 0x00, 0x48, 0x20, 0x14, 0x00, 0x44, 0x21, 0x13, 0x53, 0x41, 0x4d, 0x50, 110 | 0x4c, 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 111 | 0x20, 0x20, 0x44, 0x42, 0x32, 0x4c, 0x49, 0x43, 0x20, 0x20, 0x20, 0x20, 112 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53, 0x59, 0x53, 0x4c, 113 | 0x49, 0x43, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 114 | 0x20, 0x20, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 115 | 0x00, 0x35, 0xd0, 0x43, 0x00, 0x02, 0x00, 0x2f, 0x24, 0x14, 0x00, 0x00, 116 | 0x00, 0x00, 0x25, 0x53, 0x45, 0x54, 0x20, 0x43, 0x55, 0x52, 0x52, 0x45, 117 | 0x4e, 0x54, 0x20, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x45, 0x20, 0x4c, 0x43, 118 | 0x5f, 0x43, 0x54, 0x59, 0x50, 0x45, 0x20, 0x3d, 0x20, 0x27, 0x65, 0x6e, 119 | 0x5f, 0x55, 0x53, 0x27, 0xff, 0x00, 0x6c, 0xd0, 0x51, 0x00, 0x03, 0x00, 120 | 0x66, 0x20, 0x0b, 0x00, 0x44, 0x21, 0x13, 0x53, 0x41, 0x4d, 0x50, 0x4c, 121 | 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 122 | 0x20, 0x44, 0x42, 0x32, 0x4c, 0x49, 0x43, 0x20, 0x20, 0x20, 0x20, 0x20, 123 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53, 0x59, 0x53, 0x4c, 0x49, 124 | 0x43, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 125 | 0x20, 0x50, 0x42, 0x35, 0x4a, 0x4c, 0x46, 0x4a, 0x77, 0x00, 0x01, 0x00, 126 | 0x05, 0x21, 0x05, 0xf1, 0x00, 0x05, 0x21, 0x11, 0xf1, 0x00, 0x08, 0x21, 127 | 0x14, 0x00, 0x00, 0xff, 0xff, 0x00, 0x06, 0x21, 0x41, 0xff, 0xff, 0x00, 128 | 0x06, 0x21, 0x40, 0xff, 0xff, 0x02, 0xb4, 0xd0, 0x43, 0x00, 0x03, 0x02, 129 | 0xae, 0x24, 0x12, 0x00, 0x22, 0x00, 0x10, 0x18, 0x76, 0xd0, 0x3f, 0x00, 130 | 0x05, 0x3f, 0x00, 0x09, 0x3f, 0x00, 0x08, 0x3f, 0x00, 0x04, 0x3f, 0x00, 131 | 0x01, 0x3f, 0x00, 0x01, 0x3f, 0x02, 0x58, 0x06, 0x71, 0xe4, 0xd0, 0x00, 132 | 0x01, 0x02, 0x88, 0x14, 0x7a, 0x00, 0x00, 0x00, 0x05, 0x4e, 0x6f, 0x6b, 133 | 0x69, 0x61, 0x00, 0x00, 0x09, 0x31, 0x32, 0x37, 0x2e, 0x30, 0x2e, 0x31, 134 | 0x2e, 0x31, 0x00, 0x00, 0x08, 0x44, 0x42, 0x32, 0x49, 0x4e, 0x53, 0x54, 135 | 0x31, 0x00, 0x00, 0x04, 0x31, 0x30, 0x30, 0x35, 0xff, 0xff, 0x00, 0x02, 136 | 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 137 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 138 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 139 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 140 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 141 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 142 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 143 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 144 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 145 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 146 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 147 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 148 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 149 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 150 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 151 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 152 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 153 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 154 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 155 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 156 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 157 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 158 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 159 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 160 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 161 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 162 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 163 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 164 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 165 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 166 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 167 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 168 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 169 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 170 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 171 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 172 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 173 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 174 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 175 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 176 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 177 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 178 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 179 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 180 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 181 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 182 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 183 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 184 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 185 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 186 | 0x00, 0x00, 0x0a, 0xd0, 0x01, 0x00, 0x04, 0x00, 0x04, 0x20, 0x0e, 187 | }, 188 | { 189 | 0x00, 0x12, 0xd0, 0x43, 0x00, 0x01, 0x00, 0x0c, 0x14, 0x43, 0x00, 0x08, 190 | 0x14, 0x04, 0x14, 0xcc, 0x04, 0xb8, 0x00, 0x0b, 0xd0, 0x43, 0x00, 0x02, 191 | 0x00, 0x05, 0x24, 0x08, 0xff, 0x00, 0x79, 0xd0, 0x43, 0x00, 0x03, 0x00, 192 | 0x73, 0x24, 0x08, 0x00, 0xdb, 0xfc, 0xff, 0xff, 0x35, 0x31, 0x30, 0x30, 193 | 0x32, 0x53, 0x51, 0x4c, 0x52, 0x41, 0x31, 0x34, 0x44, 0x00, 0x6d, 0x00, 194 | 0x1a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 195 | 0x00, 0x00, 0x9c, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 196 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x12, 0x53, 197 | 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 198 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x20, 0x44, 0x42, 0x32, 0x4c, 0x49, 199 | 0x43, 0x2e, 0x53, 0x59, 0x53, 0x4c, 0x49, 0x43, 0x20, 0x30, 0x58, 0x35, 200 | 0x30, 0x34, 0x32, 0x33, 0x35, 0x34, 0x41, 0x34, 0x43, 0x34, 0x36, 0x34, 201 | 0x41, 0x37, 0x37, 0x00, 0x00, 0xff, 0x00, 0x2b, 0xd0, 0x52, 0x00, 0x04, 202 | 0x00, 0x25, 0x22, 0x0c, 0x00, 0x06, 0x11, 0x49, 0x00, 0x04, 0x00, 0x05, 203 | 0x21, 0x15, 0x01, 0x00, 0x16, 0x21, 0x10, 0x53, 0x41, 0x4d, 0x50, 0x4c, 204 | 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 205 | 0x20, 0x00, 0x0b, 0xd0, 0x03, 0x00, 0x04, 0x00, 0x05, 0x24, 0x08, 0xff, 206 | }, 207 | { 208 | 0x00, 0x0a, 0xd0, 0x01, 0x00, 0x01, 0x00, 0x04, 0x20, 0x0e, 209 | }, 210 | { 211 | 0x00, 0x2b, 0xd0, 0x52, 0x00, 0x01, 0x00, 0x25, 0x22, 0x0c, 0x00, 0x06, 212 | 0x11, 0x49, 0x00, 0x04, 0x00, 0x05, 0x21, 0x15, 0x01, 0x00, 0x16, 0x21, 213 | 0x10, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 214 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x0b, 0xd0, 0x03, 0x00, 215 | 0x01, 0x00, 0x05, 0x24, 0x08, 0xff, 216 | }, 217 | { 218 | 0x00, 0x4e, 0xd0, 0x51, 0x00, 0x01, 0x00, 0x48, 0x20, 0x14, 219 | 0x00, 0x44, 0x21, 0x13, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x20, 0x20, 220 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x4e, 0x55, 221 | 0x4c, 0x4c, 0x49, 0x44, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 222 | 0x20, 0x20, 0x20, 0x20, 0x53, 0x59, 0x53, 0x53, 0x48, 0x32, 0x30, 0x30, 223 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x01, 224 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x2d, 0xd0, 0x43, 225 | 0x00, 0x01, 0x00, 0x27, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x53, 226 | 0x45, 0x54, 0x20, 0x43, 0x4c, 0x49, 0x45, 0x4e, 0x54, 0x20, 0x57, 0x52, 227 | 0x4b, 0x53, 0x54, 0x4e, 0x4e, 0x41, 0x4d, 0x45, 0x20, 0x27, 0x4e, 0x6f, 228 | 0x6b, 0x69, 0x61, 0x27, 0xff, 0x00, 0x53, 0xd0, 0x51, 0x00, 0x02, 0x00, 229 | 0x4d, 0x20, 0x0d, 0x00, 0x44, 0x21, 0x13, 0x53, 0x41, 0x4d, 0x50, 0x4c, 230 | 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 231 | 0x20, 0x4e, 0x55, 0x4c, 0x4c, 0x49, 0x44, 0x20, 0x20, 0x20, 0x20, 0x20, 232 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53, 0x59, 0x53, 0x53, 0x48, 233 | 0x32, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 234 | 0x20, 0x53, 0x59, 0x53, 0x4c, 0x56, 0x4c, 0x30, 0x31, 0x00, 0x04, 0x00, 235 | 0x05, 0x21, 0x16, 0xf1, 0x00, 0x1a, 0xd0, 0x53, 0x00, 0x02, 0x00, 0x14, 236 | 0x24, 0x50, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x57, 0x49, 0x54, 0x48, 0x20, 237 | 0x48, 0x4f, 0x4c, 0x44, 0x20, 0xff, 0x00, 0x57, 0xd0, 0x43, 0x00, 0x02, 238 | 0x00, 0x51, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00, 0x47, 0x73, 0x65, 0x6c, 239 | 0x65, 0x63, 0x74, 0x20, 0x50, 0x43, 0x54, 0x46, 0x52, 0x45, 0x45, 0x2c, 240 | 0x20, 0x43, 0x41, 0x52, 0x44, 0x2c, 0x20, 0x43, 0x48, 0x49, 0x4c, 0x44, 241 | 0x52, 0x45, 0x4e, 0x20, 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x73, 0x79, 0x73, 242 | 0x63, 0x61, 0x74, 0x2e, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x20, 0x77, 243 | 0x68, 0x65, 0x72, 0x65, 0x20, 0x54, 0x41, 0x42, 0x4e, 0x41, 0x4d, 0x45, 244 | 0x3d, 0x27, 0x53, 0x41, 0x4c, 0x45, 0x53, 0x27, 0xff, 0x00, 0x72, 0xd0, 245 | 0x01, 0x00, 0x03, 0x00, 0x6c, 0x20, 0x0c, 0x00, 0x44, 0x21, 0x13, 0x53, 246 | 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 247 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x4e, 0x55, 0x4c, 0x4c, 0x49, 0x44, 0x20, 248 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x53, 249 | 0x59, 0x53, 0x53, 0x48, 0x32, 0x30, 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 250 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x53, 0x59, 0x53, 0x4c, 0x56, 0x4c, 0x30, 251 | 0x31, 0x00, 0x04, 0x00, 0x08, 0x21, 0x14, 0x00, 0x00, 0xff, 0xff, 0x00, 252 | 0x06, 0x21, 0x41, 0xff, 0xff, 0x00, 0x05, 0x21, 0x5d, 0x01, 0x00, 0x05, 253 | 0x21, 0x4b, 0xf1, 0x00, 0x0c, 0x21, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 254 | 0x00, 0xff, 0xff, 255 | }, 256 | { 257 | 0x00, 0x0b, 0xd0, 0x43, 0x00, 0x01, 0x00, 0x05, 0x24, 0x08, 0xff, 0x00, 258 | 0xf3, 0xd0, 0x43, 0x00, 0x02, 0x00, 0xed, 0x24, 0x11, 0x00, 0x00, 0x00, 259 | 0x00, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x53, 0x51, 0x4c, 0x31, 0x30, 260 | 0x30, 0x35, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 261 | 0x04, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 262 | 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 263 | 0x20, 0x20, 0x20, 0x00, 0x12, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x20, 264 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 265 | 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 266 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x00, 267 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 268 | 0x00, 0x07, 0x50, 0x43, 0x54, 0x46, 0x52, 0x45, 0x45, 0x00, 0x00, 0x00, 269 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 270 | 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xec, 0x01, 271 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 272 | 0x00, 0x00, 0x00, 0x00, 0x04, 0x43, 0x41, 0x52, 0x44, 0x00, 0x00, 0x00, 273 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 274 | 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf5, 0x01, 275 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 276 | 0x00, 0x00, 0x00, 0x00, 0x08, 0x43, 0x48, 0x49, 0x4c, 0x44, 0x52, 0x45, 277 | 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 278 | 0xff, 0xff, 0x00, 0x37, 0xd0, 0x52, 0x00, 0x03, 0x00, 0x31, 0x22, 0x05, 279 | 0x00, 0x06, 0x11, 0x49, 0x00, 0x00, 0x00, 0x06, 0x21, 0x02, 0x24, 0x17, 280 | 0x00, 0x05, 0x21, 0x1f, 0xf1, 0x00, 0x05, 0x21, 0x50, 0x01, 0x00, 0x0c, 281 | 0x21, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 282 | 0x21, 0x4b, 0xf1, 0x00, 0x06, 0x24, 0x60, 0x24, 0x42, 0x00, 0x25, 0xd0, 283 | 0x53, 0x00, 0x03, 0x00, 0x1f, 0x24, 0x1a, 0x0c, 0x76, 0xd0, 0x04, 0x00, 284 | 0x02, 0x16, 0x00, 0x08, 0x05, 0x00, 0x02, 0x09, 0x71, 0xe0, 0x54, 0x00, 285 | 0x01, 0xd0, 0x00, 0x01, 0x06, 0x71, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x19, 286 | 0xd0, 0x53, 0x00, 0x03, 0x00, 0x13, 0x24, 0x1b, 0xff, 0x00, 0xff, 0xff, 287 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 288 | 0x26, 0xd0, 0x52, 0x00, 0x03, 0x00, 0x20, 0x22, 0x0b, 0x00, 0x06, 0x11, 289 | 0x49, 0x00, 0x04, 0x00, 0x16, 0x21, 0x10, 0x53, 0x41, 0x4d, 0x50, 0x4c, 290 | 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 291 | 0x20, 0x00, 0x59, 0xd0, 0x03, 0x00, 0x03, 0x00, 0x53, 0x24, 0x08, 0x00, 292 | 0x64, 0x00, 0x00, 0x00, 0x30, 0x32, 0x30, 0x30, 0x30, 0x53, 0x51, 0x4c, 293 | 0x52, 0x49, 0x30, 0x31, 0x46, 0x00, 0x01, 0x00, 0x04, 0x80, 0x01, 0x00, 294 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 295 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 296 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x12, 0x53, 0x41, 0x4d, 0x50, 0x4c, 297 | 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 298 | 0x20, 0x00, 0x00, 0x00, 0x00, 0xff, 299 | }, 300 | { 301 | 0x00, 0x0a, 0xd0, 0x01, 0x00, 0x01, 0x00, 0x04, 0x20, 0x0e, 302 | }, 303 | { 304 | 0x00, 0x2b, 0xd0, 0x52, 0x00, 0x01, 0x00, 0x25, 0x22, 0x0c, 0x00, 0x06, 305 | 0x11, 0x49, 0x00, 0x04, 0x00, 0x05, 0x21, 0x15, 0x01, 0x00, 0x16, 0x21, 306 | 0x10, 0x53, 0x41, 0x4d, 0x50, 0x4c, 0x45, 0x20, 0x20, 0x20, 0x20, 0x20, 307 | 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x0b, 0xd0, 0x03, 0x00, 308 | 0x01, 0x00, 0x05, 0x24, 0x08, 0xff, 309 | }, 310 | { 311 | 0x00, 0x0a, 0xd0, 0x05, 0x00, 0x01, 0x00, 0x04, 0xc0, 0x04, 312 | }, 313 | } 314 | var db2Count int 315 | 316 | func db2DummyReader(c io.Reader) (buf []byte, err error) { 317 | if db2Count < len(sampleDB2) { 318 | buf = sampleDB2[db2Count] 319 | db2Count++ 320 | } else { 321 | err = errors.New("EOF") 322 | } 323 | return 324 | } 325 | 326 | func TestDB2(t *testing.T) { 327 | d := new(dbms.DB2) 328 | port := d.DefaultPort() 329 | if d.DefaultPort() != 50000 { 330 | t.Error("Expected 50000, got ", port) 331 | } 332 | err := d.SetCertificate("", "") 333 | if err == nil { 334 | t.Error("Expected error") 335 | } 336 | d.SetReader(db2DummyReader) 337 | var s mock.ConnMock 338 | d.SetSockets(&s, &s) 339 | err = d.Handler() 340 | if err != nil { 341 | t.Error("Got error", err) 342 | } 343 | d.Close() 344 | } 345 | -------------------------------------------------------------------------------- /dbshield/dbms/io.go: -------------------------------------------------------------------------------- 1 | package dbms 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "net" 7 | ) 8 | 9 | const ( 10 | chunkSize = 4096 11 | ) 12 | 13 | //ReadPacket all available data from socket 14 | func ReadPacket(conn io.Reader) ([]byte, error) { 15 | data := make([]byte, chunkSize) 16 | buf := bytes.Buffer{} 17 | for { 18 | n, err := conn.Read(data) 19 | if err != nil { 20 | return nil, err 21 | } 22 | buf.Write(data[:n]) 23 | if n != chunkSize { 24 | break 25 | } 26 | } 27 | return buf.Bytes(), nil 28 | } 29 | 30 | func readWrite(src, dst net.Conn, reader func(io.Reader) ([]byte, error)) error { 31 | //Read result from server 32 | buf, err := reader(src) 33 | if err != nil { 34 | return err 35 | } 36 | 37 | //Send result to client 38 | _, err = dst.Write(buf) 39 | return err 40 | } 41 | -------------------------------------------------------------------------------- /dbshield/dbms/io_test.go: -------------------------------------------------------------------------------- 1 | package dbms 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "testing" 7 | 8 | "github.com/nim4/mock" 9 | ) 10 | 11 | func TestReadWrite(t *testing.T) { 12 | var s = mock.ConnMock{Error: errors.New("Dummy Error")} 13 | err := readWrite(&s, &s, ReadPacket) 14 | if err == nil { 15 | t.Error("Expected error") 16 | } 17 | 18 | s.Error = nil 19 | err = readWrite(&s, &s, ReadPacket) 20 | if err != nil { 21 | t.Error("Got error", err) 22 | } 23 | } 24 | 25 | func BenchmarkReadPacket(b *testing.B) { 26 | var buf [1024]byte 27 | for i := 0; i < b.N; i++ { 28 | s := bytes.NewReader(buf[:]) 29 | ReadPacket(s) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /dbshield/dbms/mssql.go: -------------------------------------------------------------------------------- 1 | package dbms 2 | 3 | import ( 4 | "crypto/tls" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "net" 9 | "sync" 10 | "time" 11 | 12 | "github.com/nim4/DBShield/dbshield/logger" 13 | "github.com/nim4/DBShield/dbshield/sql" 14 | ) 15 | 16 | const maxMSSQLPayloadLen = 4096 17 | 18 | const ( 19 | mssqlEncryptionAvailableAndOff = iota 20 | mssqlEncryptionAvailableAndOn 21 | mssqlEncryptionNotAvailable 22 | mssqlEncryptionRequired 23 | ) 24 | 25 | //MSSQL DBMS 26 | type MSSQL struct { 27 | client net.Conn 28 | server net.Conn 29 | certificate tls.Certificate 30 | currentDB []byte 31 | username []byte 32 | reader func(io.Reader) ([]byte, error) 33 | } 34 | 35 | //SetCertificate to use if client asks for SSL 36 | func (m *MSSQL) SetCertificate(crt, key string) (err error) { 37 | m.certificate, err = tls.LoadX509KeyPair(crt, key) 38 | return 39 | } 40 | 41 | //SetReader function for sockets IO 42 | func (m *MSSQL) SetReader(f func(io.Reader) ([]byte, error)) { 43 | m.reader = f 44 | } 45 | 46 | //SetSockets for dbms (client and server sockets) 47 | func (m *MSSQL) SetSockets(c, s net.Conn) { 48 | m.client = c 49 | m.server = s 50 | } 51 | 52 | //Close sockets 53 | func (m *MSSQL) Close() { 54 | defer handlePanic() 55 | m.client.Close() 56 | m.server.Close() 57 | } 58 | 59 | //DefaultPort of the DBMS 60 | func (m *MSSQL) DefaultPort() uint { 61 | return 1433 62 | } 63 | 64 | //Handler gets incoming requests 65 | func (m *MSSQL) Handler() error { 66 | defer handlePanic() 67 | defer m.Close() 68 | 69 | success, err := m.handleLogin() 70 | if err != nil { 71 | return err 72 | } 73 | if !success { 74 | logger.Warning("Login failed") 75 | return nil 76 | } 77 | for { 78 | var buf []byte 79 | buf, err = m.reader(m.client) 80 | if err != nil || len(buf) < 8 { 81 | return err 82 | } 83 | 84 | switch buf[0] { 85 | case 0x01: //SQL batch 86 | query := buf[8:] 87 | context := sql.QueryContext{ 88 | Query: query, 89 | Database: m.currentDB, 90 | User: m.username, 91 | Client: remoteAddrToIP(m.client.RemoteAddr()), 92 | Time: time.Now(), 93 | } 94 | processContext(context) 95 | } 96 | 97 | //Send query/request to server 98 | _, err = m.server.Write(buf) 99 | if err != nil { 100 | return err 101 | } 102 | //Recive response 103 | err = readWrite(m.server, m.client, m.reader) 104 | if err != nil { 105 | return err 106 | } 107 | } 108 | } 109 | 110 | func (m *MSSQL) handleLogin() (success bool, err error) { 111 | 112 | //Receive PreLogin Request 113 | buf, err := m.reader(m.client) 114 | if err != nil { 115 | return 116 | } 117 | 118 | if buf[0] != 0x12 { 119 | err = errors.New("packet is not PRELOGIN") 120 | return 121 | } 122 | 123 | //Send PreLogin to server 124 | _, err = m.server.Write(buf) 125 | if err != nil { 126 | return 127 | } 128 | 129 | //Receive PRELOGIN response 130 | buf, err = m.reader(m.server) 131 | if err != nil { 132 | return 133 | } 134 | 135 | if buf[0] != 0x4 { 136 | err = errors.New("packet is not PRELOGIN response") 137 | return 138 | } 139 | 140 | //Send PreLogin to server 141 | _, err = m.client.Write(buf) 142 | if err != nil { 143 | return 144 | } 145 | 146 | //Set data to beginning of the prelogin message 147 | data := buf[8:] 148 | 149 | var encryption byte 150 | 151 | //Lookup Encryption 152 | for i := 0; i < len(data); i += 5 { 153 | switch data[i] { 154 | case 0x01: //Encryption 155 | encryption = data[int(data[i+1])] 156 | break 157 | case 0xff: //Terminator 158 | break 159 | } 160 | } 161 | logger.Debugf("Encryption: %v", encryption) 162 | 163 | // buf, err = m.reader(m.client) 164 | // if err != nil { 165 | // return 166 | // } 167 | // 168 | //m.username, m.currentDB = MSSQLGetUsernameDB(buf) 169 | 170 | for { 171 | //Receive PreLogin Request 172 | err = readWrite(m.client, m.server, m.reader) 173 | if err != nil { 174 | return 175 | } 176 | 177 | //Receive PRELOGIN response 178 | buf, err = m.reader(m.server) 179 | if err != nil { 180 | return 181 | } 182 | 183 | //Send PreLogin to server 184 | _, err = m.client.Write(buf) 185 | if err != nil { 186 | return 187 | } 188 | 189 | if buf[0] == 0x4 { 190 | break 191 | } 192 | } 193 | success = true 194 | return 195 | } 196 | 197 | //buffer pool for MSSQLReadPacket 198 | var mssqlDataPool = sync.Pool{ 199 | New: func() interface{} { 200 | return make([]byte, maxMSSQLPayloadLen) 201 | }, 202 | } 203 | 204 | //MSSQLReadPacket handles reading mysql packets 205 | func MSSQLReadPacket(src io.Reader) ([]byte, error) { 206 | data := mssqlDataPool.Get().([]byte) 207 | defer mssqlDataPool.Put(data) 208 | n, err := src.Read(data) 209 | if err != nil && err != io.EOF { 210 | return nil, err 211 | } 212 | 213 | if n < maxMSSQLPayloadLen || err == io.EOF { 214 | return data[:n], nil 215 | } 216 | 217 | buf, err := MSSQLReadPacket(src) 218 | if err != nil { 219 | return nil, err 220 | } 221 | return append(data, buf...), nil 222 | } 223 | 224 | //MSSQLGetUsernameDB parse packet and gets username and db name 225 | func MSSQLGetUsernameDB(data []byte) (username, db []byte) { 226 | //TODO: Extract Username and db name 227 | fmt.Printf("%x", data) 228 | return 229 | } 230 | -------------------------------------------------------------------------------- /dbshield/dbms/mssql_test.go: -------------------------------------------------------------------------------- 1 | package dbms_test 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "io" 7 | "testing" 8 | 9 | "github.com/nim4/DBShield/dbshield/dbms" 10 | "github.com/nim4/mock" 11 | ) 12 | 13 | var sampleMSSQL = [...][]byte{ 14 | { 15 | 0x12, 0x01, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 16 | 0x06, 0x01, 0x00, 0x1b, 0x00, 0x01, 0x02, 0x00, 0x1c, 0x00, 0x01, 0x03, 17 | 0x00, 0x1d, 0x00, 0x04, 0xff, 0x08, 0x00, 0x01, 0x55, 0x00, 0x00, 0x00, 18 | 0x00, 0x33, 0x00, 0x00, 0x00, 19 | }, //Client 20 | { 21 | 0x04, 0x01, 0x00, 0x25, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x15, 0x00, 22 | 0x06, 0x01, 0x00, 0x1b, 0x00, 0x01, 0x02, 0x00, 0x1c, 0x00, 0x01, 0x03, 23 | 0x00, 0x1d, 0x00, 0x00, 0xff, 0x0e, 0x00, 0x01, 0x95, 0x00, 0x00, 0x00, 24 | 0x00, 25 | }, 26 | { 27 | 0x12, 0x01, 0x00, 0x8a, 0x00, 0x00, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, 28 | 0x7d, 0x01, 0x00, 0x00, 0x79, 0x03, 0x01, 0x58, 0xe0, 0x1c, 0x92, 0x11, 29 | 0xd8, 0x8b, 0x54, 0x35, 0x61, 0x51, 0x35, 0x9c, 0xd9, 0x67, 0x01, 0xd3, 30 | 0xae, 0xfd, 0x61, 0xfb, 0x8e, 0x46, 0xf1, 0x4a, 0x05, 0x34, 0x91, 0xf0, 31 | 0xde, 0xa8, 0x25, 0x00, 0x00, 0x20, 0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x08, 32 | 0xc0, 0x14, 0xc0, 0x13, 0xc0, 0x12, 0x00, 0x35, 0x00, 0x84, 0x00, 0x2f, 33 | 0x00, 0x41, 0x00, 0x0a, 0x00, 0x39, 0x00, 0x88, 0x00, 0x33, 0x00, 0x45, 34 | 0x00, 0x16, 0x01, 0x00, 0x00, 0x30, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 35 | 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 36 | 0x01, 0x00, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x0c, 37 | 0x00, 0x0a, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x15, 0x00, 0x13, 38 | 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 39 | }, 40 | { 41 | 0x12, 0x01, 0x04, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x16, 0x03, 0x01, 0x04, 42 | 0xb0, 0x02, 0x00, 0x00, 0x51, 0x03, 0x01, 0x58, 0xe0, 0x1c, 0xbb, 0x62, 43 | 0x3c, 0x46, 0x84, 0x07, 0x4c, 0x72, 0xf5, 0x7c, 0x94, 0xaa, 0xeb, 0x3e, 44 | 0x21, 0xe8, 0x60, 0x2f, 0x65, 0x61, 0xa0, 0x36, 0x46, 0x8a, 0x54, 0x04, 45 | 0x81, 0xc9, 0xcf, 0x20, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 46 | 0x47, 0x3c, 0x79, 0x6e, 0xca, 0x05, 0xa3, 0xe6, 0xea, 0x4d, 0x34, 0x3a, 47 | 0x54, 0x71, 0xbf, 0x5b, 0x3e, 0x08, 0xc3, 0x1f, 0x1f, 0xb5, 0xbc, 0x72, 48 | 0xc0, 0x14, 0x00, 0x00, 0x09, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 49 | 0x01, 0x00, 0x0b, 0x00, 0x03, 0x08, 0x00, 0x03, 0x05, 0x00, 0x03, 0x02, 50 | 0x30, 0x82, 0x02, 0xfe, 0x30, 0x82, 0x01, 0xe6, 0xa0, 0x03, 0x02, 0x01, 51 | 0x02, 0x02, 0x10, 0x55, 0xf6, 0x58, 0xc8, 0x57, 0x53, 0x61, 0xb4, 0x4c, 52 | 0xe4, 0x1e, 0xbb, 0xb9, 0x6e, 0x4b, 0x0a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 53 | 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x3b, 54 | 0x31, 0x39, 0x30, 0x37, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1e, 0x30, 0x00, 55 | 0x53, 0x00, 0x53, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x65, 0x00, 56 | 0x6c, 0x00, 0x66, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x69, 0x00, 0x67, 0x00, 57 | 0x6e, 0x00, 0x65, 0x00, 0x64, 0x00, 0x5f, 0x00, 0x46, 0x00, 0x61, 0x00, 58 | 0x6c, 0x00, 0x6c, 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x30, 59 | 0x1e, 0x17, 0x0d, 0x31, 0x37, 0x30, 0x34, 0x30, 0x31, 0x31, 0x37, 0x33, 60 | 0x35, 0x31, 0x30, 0x5a, 0x17, 0x0d, 0x34, 0x37, 0x30, 0x34, 0x30, 0x31, 61 | 0x31, 0x37, 0x33, 0x35, 0x31, 0x30, 0x5a, 0x30, 0x3b, 0x31, 0x39, 0x30, 62 | 0x37, 0x06, 0x03, 0x55, 0x04, 0x03, 0x1e, 0x30, 0x00, 0x53, 0x00, 0x53, 63 | 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x66, 64 | 0x00, 0x5f, 0x00, 0x53, 0x00, 0x69, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x65, 65 | 0x00, 0x64, 0x00, 0x5f, 0x00, 0x46, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x6c, 66 | 0x00, 0x62, 0x00, 0x61, 0x00, 0x63, 0x00, 0x6b, 0x30, 0x82, 0x01, 0x22, 67 | 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 68 | 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 69 | 0x02, 0x82, 0x01, 0x01, 0x00, 0xda, 0x2b, 0xf1, 0xd1, 0xc1, 0xd2, 0x17, 70 | 0x23, 0xe1, 0xff, 0xa0, 0xdd, 0x91, 0x3d, 0xfb, 0x80, 0x8e, 0xec, 0x67, 71 | 0x45, 0x49, 0x03, 0x6b, 0xea, 0x4b, 0xbf, 0x4f, 0x5e, 0x77, 0xc1, 0x8e, 72 | 0x1e, 0xdc, 0xd8, 0x7f, 0x64, 0x03, 0xc3, 0x1d, 0xd3, 0x43, 0xe8, 0x6e, 73 | 0x7b, 0x1c, 0x9c, 0x08, 0x2d, 0x6b, 0x02, 0x7a, 0xc2, 0xed, 0x03, 0xb4, 74 | 0xab, 0x6e, 0x96, 0x18, 0xb3, 0x91, 0x48, 0xe0, 0xb4, 0xb6, 0x41, 0x00, 75 | 0xd6, 0x73, 0xc3, 0xa8, 0xe1, 0x98, 0xf0, 0x8b, 0xda, 0xfc, 0xf0, 0x50, 76 | 0x2b, 0xe5, 0x11, 0xb5, 0xef, 0xf3, 0xa5, 0xe3, 0x24, 0xc2, 0xeb, 0x1b, 77 | 0x3f, 0x96, 0x36, 0x3d, 0x18, 0x45, 0xfc, 0x22, 0x64, 0x77, 0x7d, 0x02, 78 | 0x1e, 0xe2, 0x98, 0xb7, 0xd1, 0x2f, 0x16, 0xe6, 0x0e, 0xa3, 0x45, 0x33, 79 | 0x7d, 0x43, 0x54, 0x3c, 0x2c, 0xa0, 0xfb, 0x78, 0x71, 0x9d, 0x8e, 0xde, 80 | 0x87, 0x59, 0x5a, 0x2c, 0xc8, 0xcc, 0x30, 0x4d, 0xb3, 0x87, 0xb0, 0xab, 81 | 0x72, 0xb1, 0x24, 0x37, 0x57, 0xc0, 0xd7, 0x20, 0x4a, 0x77, 0xa5, 0xe2, 82 | 0x93, 0xa6, 0x43, 0x98, 0x58, 0xeb, 0x51, 0x45, 0x19, 0x4e, 0x86, 0x9b, 83 | 0xa5, 0x7a, 0x2e, 0xc5, 0xe4, 0x91, 0xa2, 0x51, 0x20, 0xe3, 0x29, 0x82, 84 | 0xb7, 0x75, 0x7e, 0x4c, 0xa8, 0x6d, 0x64, 0x7a, 0x60, 0x4d, 0xb4, 0x8c, 85 | 0xed, 0x52, 0x37, 0xb6, 0x75, 0x07, 0x7f, 0x4e, 0xca, 0x2a, 0x58, 0x1d, 86 | 0xfa, 0xad, 0xc3, 0x99, 0x65, 0x04, 0x6f, 0x25, 0x43, 0x3c, 0xc2, 0x3e, 87 | 0x06, 0xc0, 0x7e, 0x87, 0xd6, 0x1e, 0x3a, 0x55, 0x1f, 0x70, 0xd3, 0x65, 88 | 0xe3, 0xfc, 0xfe, 0x0b, 0x42, 0xb8, 0x02, 0x71, 0x13, 0x86, 0x1f, 0xe0, 89 | 0xdd, 0x13, 0xb9, 0x60, 0xc7, 0xdb, 0x96, 0x45, 0xe0, 0x12, 0xe3, 0xcd, 90 | 0xf5, 0x41, 0x20, 0x68, 0x56, 0xd1, 0x9d, 0x95, 0x01, 0x02, 0x03, 0x01, 91 | 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 92 | 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x21, 0x2d, 93 | 0x3d, 0x77, 0x60, 0xed, 0x8d, 0x95, 0xb4, 0x73, 0xb6, 0x3e, 0xcb, 0x80, 94 | 0x40, 0x17, 0x1c, 0xd5, 0x5c, 0xf0, 0x7b, 0x92, 0x85, 0x17, 0xb3, 0x50, 95 | 0x79, 0x87, 0xe3, 0x36, 0xfc, 0x1d, 0xfd, 0x45, 0x5f, 0x59, 0x3f, 0xc2, 96 | 0xd2, 0x21, 0x6b, 0x32, 0xfd, 0xc9, 0xfc, 0x53, 0x22, 0x41, 0xed, 0x0a, 97 | 0xfb, 0xdb, 0xdb, 0x26, 0xef, 0xe9, 0x52, 0x53, 0xed, 0xff, 0x10, 0xf8, 98 | 0xd6, 0xaf, 0x43, 0x33, 0x5a, 0xf8, 0xe4, 0xd3, 0x70, 0x56, 0x00, 0x14, 99 | 0xca, 0xce, 0xe3, 0x9e, 0xf8, 0xe9, 0x60, 0xdd, 0xc1, 0x4b, 0x8e, 0x29, 100 | 0x14, 0x53, 0x96, 0xbd, 0x96, 0x15, 0x52, 0xf1, 0x2f, 0xb9, 0x02, 0xb1, 101 | 0x21, 0xe0, 0x0e, 0x61, 0xb7, 0xcd, 0x41, 0x6c, 0x68, 0x38, 0x79, 0xea, 102 | 0x4a, 0x16, 0x59, 0x5a, 0x1b, 0xaa, 0x8e, 0x32, 0x54, 0xae, 0xe3, 0x06, 103 | 0xa3, 0xe2, 0x58, 0x8b, 0xba, 0xda, 0xad, 0x6b, 0xf6, 0xdc, 0x94, 0x47, 104 | 0x64, 0x53, 0xf5, 0x94, 0x0a, 0x6e, 0x39, 0xc5, 0x61, 0xd2, 0xeb, 0xc5, 105 | 0xbe, 0x42, 0x77, 0x9a, 0xf1, 0x89, 0x25, 0x11, 0x5d, 0x23, 0x9c, 0xc2, 106 | 0x7e, 0xe0, 0x57, 0xee, 0xa6, 0x85, 0x86, 0x90, 0x1d, 0x95, 0x1b, 0x80, 107 | 0x05, 0xee, 0xc9, 0x9a, 0xed, 0xbc, 0x2d, 0x3d, 0x89, 0xf8, 0xde, 0x2b, 108 | 0x99, 0x0a, 0x87, 0xa8, 0x63, 0x8b, 0xe5, 0xbf, 0x57, 0xf1, 0xf4, 0x3c, 109 | 0xe6, 0xaa, 0xbf, 0xe8, 0x6a, 0xe3, 0xa7, 0x82, 0x79, 0x99, 0x8e, 0x76, 110 | 0xa3, 0x32, 0x66, 0xb0, 0xfd, 0x05, 0xca, 0xeb, 0x13, 0x56, 0x50, 0xb5, 111 | 0x33, 0x84, 0x93, 0xe0, 0x99, 0x53, 0x37, 0xa0, 0x30, 0x42, 0xc7, 0xf2, 112 | 0xe2, 0x61, 0xe6, 0xc6, 0xbd, 0xbf, 0xdd, 0xbd, 0x14, 0xd3, 0x21, 0x8e, 113 | 0x28, 0xb1, 0xc5, 0xfe, 0xf5, 0x9c, 0x3c, 0x2a, 0x6d, 0x26, 0xe8, 0x03, 114 | 0x4b, 0xf0, 0x0c, 0x00, 0x01, 0x47, 0x03, 0x00, 0x17, 0x41, 0x04, 0xc4, 115 | 0x12, 0x3b, 0x5a, 0x3d, 0x6e, 0x3a, 0xac, 0x10, 0xd5, 0xee, 0xff, 0x13, 116 | 0xad, 0x8d, 0xdf, 0x90, 0x34, 0x0d, 0x40, 0xe5, 0x54, 0x98, 0x39, 0x3b, 117 | 0xd5, 0xab, 0xe8, 0x47, 0x64, 0xb9, 0x47, 0xad, 0x85, 0x9e, 0x42, 0x8e, 118 | 0x77, 0xec, 0x25, 0x90, 0xed, 0x88, 0x87, 0x02, 0x80, 0x46, 0x3b, 0x18, 119 | 0x5f, 0xf1, 0xa0, 0xde, 0x1b, 0x4c, 0x9d, 0xb8, 0x7d, 0x3c, 0x5a, 0x55, 120 | 0x95, 0x14, 0xf9, 0x01, 0x00, 0x3c, 0x28, 0x0e, 0xbf, 0x03, 0x0f, 0x2e, 121 | 0x44, 0x41, 0x7c, 0x20, 0xa0, 0xd0, 0xf2, 0x42, 0xaf, 0x6b, 0x0b, 0x1d, 122 | 0x9f, 0xa4, 0x7b, 0xce, 0x4e, 0xfa, 0x19, 0x68, 0x08, 0x95, 0x8d, 0x34, 123 | 0x1d, 0x9a, 0x63, 0x05, 0x22, 0xa4, 0xb0, 0x2e, 0x59, 0xaa, 0x45, 0x22, 124 | 0x55, 0x68, 0x62, 0x6a, 0xab, 0xeb, 0x9a, 0xa6, 0x37, 0xb8, 0x41, 0x2a, 125 | 0x12, 0xd8, 0xa3, 0x87, 0x86, 0x06, 0x9d, 0xb5, 0x16, 0x13, 0xa9, 0x92, 126 | 0xb8, 0x0d, 0xc2, 0x06, 0x5b, 0xa0, 0xde, 0x4d, 0x64, 0x93, 0x97, 0xab, 127 | 0x23, 0x59, 0xd5, 0x2e, 0xb9, 0x31, 0xe0, 0xf7, 0x99, 0x71, 0x93, 0xc3, 128 | 0xce, 0xb8, 0xa1, 0x9f, 0xd7, 0x0a, 0xb0, 0x0e, 0x1d, 0xd8, 0xae, 0xea, 129 | 0xb7, 0x15, 0xfd, 0xde, 0x57, 0xb8, 0xc2, 0x92, 0x89, 0x15, 0xed, 0xf8, 130 | 0x30, 0x57, 0x7b, 0x74, 0x8b, 0xdf, 0xfb, 0x8f, 0xc8, 0xf5, 0x95, 0x82, 131 | 0x12, 0x77, 0x7a, 0x0f, 0xe3, 0x78, 0x47, 0x0b, 0x25, 0x87, 0xc8, 0xcd, 132 | 0xbf, 0x3d, 0xd1, 0x1f, 0x5b, 0xc4, 0x7f, 0x18, 0x67, 0x12, 0x38, 0x8d, 133 | 0x77, 0xb5, 0x60, 0xbb, 0xde, 0x78, 0x1c, 0x59, 0xc3, 0x00, 0x0b, 0x39, 134 | 0x15, 0x4a, 0x2e, 0xdf, 0x15, 0x6a, 0x97, 0x24, 0x63, 0x80, 0xc3, 0xa1, 135 | 0xae, 0x37, 0x66, 0x46, 0xc2, 0xc0, 0x73, 0xe5, 0xb8, 0x70, 0x7a, 0x83, 136 | 0x81, 0x06, 0xc5, 0x81, 0xd3, 0x1e, 0xe0, 0xcc, 0x84, 0x25, 0x6e, 0x9e, 137 | 0xb9, 0x54, 0x9b, 0x4e, 0x3b, 0x07, 0x1d, 0xd5, 0x6e, 0xd1, 0x59, 0xd8, 138 | 0xed, 0x9d, 0xce, 0x86, 0xe0, 0x08, 0xdb, 0xbb, 0xb7, 0x9c, 0x79, 0x7b, 139 | 0xb0, 0x9c, 0x26, 0x0a, 0x9a, 0x21, 0xce, 0x45, 0x6d, 0x74, 0x42, 0x3d, 140 | 0x34, 0x85, 0x5f, 0x6c, 0x1f, 0x3d, 0x1b, 0x4d, 0x2a, 0xaf, 0x02, 0x19, 141 | 0xe4, 0xc5, 0x76, 0x28, 0x8d, 0x2d, 0xe4, 0xf5, 0x77, 0x0e, 0x00, 0x00, 142 | 0x00, 143 | }, 144 | { 145 | 0x12, 0x01, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x16, 0x03, 0x01, 0x00, 146 | 0x46, 0x10, 0x00, 0x00, 0x42, 0x41, 0x04, 0x50, 0xf1, 0xf7, 0x5d, 0x67, 147 | 0x4b, 0xb1, 0x87, 0x38, 0x6c, 0x5d, 0x67, 0x17, 0xdf, 0x8f, 0x84, 0x26, 148 | 0xab, 0x23, 0x4d, 0x09, 0x9c, 0xe7, 0x7a, 0x00, 0xc3, 0x67, 0x9f, 0xfb, 149 | 0x37, 0x71, 0xe7, 0x73, 0xbf, 0x96, 0x6e, 0x12, 0x21, 0x56, 0x3f, 0xfc, 150 | 0xb2, 0xc2, 0xff, 0xb9, 0x59, 0x18, 0x52, 0x40, 0x3d, 0xf9, 0x04, 0x95, 151 | 0xdd, 0xbb, 0x1a, 0xe4, 0xd9, 0x3b, 0xe2, 0x93, 0xf6, 0xe9, 0xbb, 0x14, 152 | 0x03, 0x01, 0x00, 0x01, 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0xd6, 0xbf, 153 | 0x92, 0x46, 0xed, 0x89, 0xd3, 0x01, 0x60, 0x47, 0xae, 0xad, 0xfb, 0x36, 154 | 0x29, 0xe9, 0xf3, 0x82, 0x81, 0xc0, 0x0c, 0x25, 0xb9, 0x6a, 0x82, 0xa4, 155 | 0x72, 0xbc, 0x64, 0xde, 0xa4, 0x0f, 0x42, 0x02, 0x52, 0x69, 0x44, 0x95, 156 | 0xa4, 0x9e, 0x83, 0xba, 0xcd, 0xf9, 0xd9, 0x28, 0xa1, 0x89, 157 | }, 158 | { 159 | 0x12, 0x01, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x14, 0x03, 0x01, 0x00, 160 | 0x01, 0x01, 0x16, 0x03, 0x01, 0x00, 0x30, 0x41, 0x48, 0xea, 0x44, 0xcb, 161 | 0xed, 0x85, 0x85, 0xeb, 0x1e, 0xa8, 0x2e, 0xf5, 0xec, 0xf3, 0xfe, 0x2c, 162 | 0xb7, 0x3b, 0x2c, 0x1e, 0xed, 0xac, 0xd8, 0x66, 0x8e, 0x12, 0x01, 0x57, 163 | 0x00, 0xcc, 0x13, 0xe5, 0xa1, 0xb1, 0x9d, 0x7c, 0x7c, 0x56, 0x25, 0x7d, 164 | 0x39, 0x7e, 0x11, 0x09, 0x84, 0x9a, 0x33, 165 | }, 166 | { 167 | 0x17, 0x03, 0x01, 0x00, 0xd0, 0x7e, 0x3a, 0xf5, 0xc4, 0x5b, 0x67, 0x81, 168 | 0x8f, 0xee, 0xd0, 0x67, 0x72, 0x9a, 0x1b, 0xfa, 0xc5, 0x2a, 0x13, 0x6f, 169 | 0x3e, 0x61, 0xe5, 0x8c, 0x3d, 0xd4, 0x32, 0x81, 0x70, 0xc3, 0xc8, 0x3c, 170 | 0x68, 0x90, 0x71, 0x9c, 0xf1, 0xc9, 0x8f, 0x62, 0x6d, 0xb4, 0xb3, 0xff, 171 | 0x8a, 0x18, 0x99, 0x72, 0x3b, 0x10, 0x8e, 0x80, 0xaf, 0x5e, 0xe6, 0xe9, 172 | 0x67, 0x96, 0x4c, 0x2f, 0xfe, 0x69, 0xb0, 0x1c, 0xa8, 0xd9, 0x89, 0xb7, 173 | 0x78, 0x2c, 0x82, 0xb5, 0x4e, 0x39, 0x17, 0x1d, 0x4b, 0x28, 0xe8, 0x05, 174 | 0x09, 0x18, 0x8d, 0x1f, 0xaa, 0xf7, 0x7e, 0x25, 0xf6, 0x2e, 0xc2, 0x8c, 175 | 0x2e, 0x16, 0xd8, 0xfe, 0xe0, 0x78, 0x70, 0x17, 0x74, 0x6b, 0x9b, 0x29, 176 | 0x14, 0xae, 0xbd, 0x82, 0x7d, 0x13, 0x78, 0x90, 0x28, 0x31, 0xf5, 0x9c, 177 | 0x37, 0x66, 0xb4, 0x1b, 0x2f, 0x9a, 0x9f, 0xe7, 0xe0, 0x7c, 0x39, 0x79, 178 | 0x5b, 0x71, 0xa3, 0xa1, 0x21, 0xbd, 0xbb, 0x69, 0xd8, 0x59, 0x6c, 0x56, 179 | 0x5a, 0x5d, 0xea, 0x88, 0x30, 0xf3, 0x39, 0xe2, 0x8a, 0x44, 0x62, 0x7a, 180 | 0x43, 0xb3, 0x06, 0xc6, 0x88, 0x49, 0x80, 0x5c, 0x30, 0x0c, 0xe5, 0xc8, 181 | 0x44, 0xd6, 0x89, 0x08, 0x41, 0x8c, 0x2a, 0x1b, 0x73, 0x12, 0xe7, 0x97, 182 | 0xbb, 0x1a, 0x08, 0x6b, 0xbf, 0xd6, 0x6e, 0xde, 0x70, 0xc2, 0x60, 0x9f, 183 | 0x1c, 0x56, 0xda, 0xca, 0x59, 0x44, 0xfb, 0xb5, 0xc5, 0x92, 0xa4, 0x5e, 184 | 0x37, 0x5e, 0x55, 0x11, 0x7f, 0x74, 0x87, 0x5a, 0x83, 185 | }, 186 | { 187 | 0x04, 0x01, 0x01, 0x89, 0x00, 0x33, 0x01, 0x00, 0xe3, 0x1b, 0x00, 0x01, 188 | 0x06, 0x6d, 0x00, 0x61, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 189 | 0x00, 0x06, 0x6d, 0x00, 0x61, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 190 | 0x72, 0x00, 0xab, 0x6e, 0x00, 0x45, 0x16, 0x00, 0x00, 0x02, 0x00, 0x25, 191 | 0x00, 0x43, 0x00, 0x68, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x65, 192 | 0x00, 0x64, 0x00, 0x20, 0x00, 0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x61, 193 | 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x65, 0x00, 0x20, 0x00, 0x63, 194 | 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x78, 0x00, 0x74, 195 | 0x00, 0x20, 0x00, 0x74, 0x00, 0x6f, 0x00, 0x20, 0x00, 0x27, 0x00, 0x6d, 196 | 0x00, 0x61, 0x00, 0x73, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x27, 197 | 0x00, 0x2e, 0x00, 0x0c, 0x39, 0x00, 0x31, 0x00, 0x66, 0x00, 0x63, 0x00, 198 | 0x30, 0x00, 0x35, 0x00, 0x62, 0x00, 0x30, 0x00, 0x33, 0x00, 0x66, 0x00, 199 | 0x63, 0x00, 0x31, 0x00, 0x00, 0x01, 0x00, 0xe3, 0x08, 0x00, 0x07, 0x05, 200 | 0x09, 0x04, 0xd0, 0x00, 0x34, 0x00, 0xe3, 0x17, 0x00, 0x02, 0x0a, 0x75, 201 | 0x00, 0x73, 0x00, 0x5f, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x6c, 202 | 0x00, 0x69, 0x00, 0x73, 0x00, 0x68, 0x00, 0x00, 0xab, 0x72, 0x00, 0x47, 203 | 0x16, 0x00, 0x00, 0x01, 0x00, 0x27, 0x00, 0x43, 0x00, 0x68, 0x00, 0x61, 204 | 0x00, 0x6e, 0x00, 0x67, 0x00, 0x65, 0x00, 0x64, 0x00, 0x20, 0x00, 0x6c, 205 | 0x00, 0x61, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x75, 0x00, 0x61, 0x00, 0x67, 206 | 0x00, 0x65, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x74, 0x00, 0x74, 207 | 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x20, 0x00, 0x74, 0x00, 0x6f, 208 | 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x5f, 0x00, 0x65, 0x00, 0x6e, 209 | 0x00, 0x67, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x68, 0x00, 0x2e, 210 | 0x00, 0x0c, 0x39, 0x00, 0x31, 0x00, 0x66, 0x00, 0x63, 0x00, 0x30, 0x00, 211 | 0x35, 0x00, 0x62, 0x00, 0x30, 0x00, 0x33, 0x00, 0x66, 0x00, 0x63, 0x00, 212 | 0x31, 0x00, 0x00, 0x01, 0x00, 0xad, 0x36, 0x00, 0x01, 0x71, 0x00, 0x00, 213 | 0x01, 0x16, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f, 0x00, 214 | 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x53, 0x00, 215 | 0x51, 0x00, 0x4c, 0x00, 0x20, 0x00, 0x53, 0x00, 0x65, 0x00, 0x72, 0x00, 216 | 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 217 | 0x01, 0x95, 0xe3, 0x13, 0x00, 0x04, 0x04, 0x34, 0x00, 0x30, 0x00, 0x39, 218 | 0x00, 0x36, 0x00, 0x04, 0x34, 0x00, 0x30, 0x00, 0x39, 0x00, 0x36, 0x00, 219 | 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 220 | }, 221 | { 222 | 0x01, 0x01, 0x00, 0x18, 0x00, 0x00, 0x01, 0x00, 0x53, 0x00, 0x45, 0x00, 223 | 0x4c, 0x00, 0x45, 0x00, 0x43, 0x00, 0x54, 0x00, 0x20, 0x00, 0x31, 0x00, 224 | }, 225 | { 226 | 0x04, 0x01, 0x00, 0x1f, 0x00, 0x33, 0x01, 0x00, 0x81, 0x01, 0x00, 0x00, 227 | 0x00, 0x20, 0x00, 0x38, 0x00, 0xd1, 0x01, 0x00, 0x00, 0x00, 0xfd, 0x10, 228 | 0x00, 0xc1, 0x00, 0x01, 0x00, 0x00, 0x00, 229 | }, 230 | } 231 | 232 | var mssqlCount int 233 | 234 | func mssqlDummyReader(c io.Reader) (buf []byte, err error) { 235 | if mssqlCount < len(sampleMSSQL) { 236 | buf = sampleMSSQL[mssqlCount] 237 | mssqlCount++ 238 | } else { 239 | err = errors.New("EOF") 240 | } 241 | return 242 | } 243 | 244 | func TestMSSQL(t *testing.T) { 245 | mssqlCount = 0 246 | var m = dbms.MSSQL{} 247 | port := m.DefaultPort() 248 | if m.DefaultPort() != 1433 { 249 | t.Error("Expected 1433, got ", port) 250 | } 251 | err := m.SetCertificate("", "") 252 | if err == nil { 253 | t.Error("Expected error") 254 | } 255 | m.SetReader(mssqlDummyReader) 256 | var s mock.ConnMock 257 | m.SetSockets(&s, &s) 258 | err = m.Handler() 259 | if err != nil && err.Error() != "EOF" { 260 | t.Error("Got error", err) 261 | } 262 | m.Close() 263 | } 264 | 265 | func BenchmarkMSSQL(b *testing.B) { 266 | b.ReportAllocs() 267 | b.ResetTimer() 268 | var s mock.ConnMock 269 | var m = dbms.MSSQL{} 270 | m.SetReader(mssqlDummyReader) 271 | m.SetSockets(&s, &s) 272 | for i := 0; i < b.N; i++ { 273 | mssqlCount = 0 274 | err := m.Handler() 275 | if err != nil { 276 | b.Fatal(err) 277 | } 278 | m.Close() 279 | } 280 | } 281 | 282 | // func TestMSSQLGetUsernameDB(t *testing.T) { 283 | // 284 | // u, d := dbms.MSSQLGetUsernameDB([]byte{ 285 | // 5, 162, 43, 0, 1, 0, 0, 0, 45, 0, 0, 0, 286 | // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 287 | // 0, 0, 0, 0, 119, 112, 0, 20, 220, 242, 53, 124, 75, 14, 62, 51, 288 | // 39, 210, 196, 162, 213, 205, 209, 232, 229, 71, 70, 62, 109, 121, 115, 113, 289 | // 108, 95, 110, 97, 116, 105, 118, 101, 95, 112, 97, 115, 115, 119, 111, 114, 290 | // 100, 0, 291 | // }) 292 | // if string(u) != "wp" { 293 | // t.Error("Expected 'wp' username got", string(u)) 294 | // } 295 | // if len(d) != 0 { 296 | // t.Error("Expected empty db name got", string(d)) 297 | // } 298 | // 299 | // u, d = dbms.MSSQLGetUsernameDB([]byte{ 300 | // 13, 162, 43, 0, 1, 0, 0, 0, 45, 0, 0, 0, 301 | // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 302 | // 0, 0, 0, 0, 119, 112, 0, 20, 0, 205, 124, 7, 125, 243, 111, 168, 303 | // 162, 10, 54, 115, 147, 159, 57, 126, 109, 123, 162, 138, 119, 112, 0, 109, 304 | // 121, 115, 113, 108, 95, 110, 97, 116, 105, 118, 101, 95, 112, 97, 115, 115, 305 | // 119, 111, 114, 100, 0, 306 | // }) 307 | // 308 | // if string(u) != "wp" { 309 | // t.Error("Expected 'wp' username got", string(u)) 310 | // } 311 | // if string(u) != "wp" { 312 | // t.Error("Expected 'wp' db name", string(d)) 313 | // } 314 | // 315 | // u, d = dbms.MSSQLGetUsernameDB([]byte{ 316 | // 13, 162, 43, 0, 1, 0, 0, 0, 45, 0, 0, 0, 317 | // 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 318 | // 0, 0, 0, 0, 319 | // }) 320 | // 321 | // if len(u) != 0 || len(d) != 0 { 322 | // t.Error("Expected empty username & db name got", string(u), string(d)) 323 | // } 324 | // } 325 | 326 | func TestMSSQLReadPacket(t *testing.T) { 327 | const maxPayloadLen = 1<<24 - 1 328 | var buf [maxPayloadLen]byte 329 | reader := bytes.NewReader(buf[:]) 330 | b, err := dbms.MSSQLReadPacket(reader) 331 | if err != nil { 332 | t.Error("Got error", err) 333 | } 334 | if bytes.Compare(b, buf[:]) != 0 { 335 | t.Error("Unexpected output") 336 | } 337 | var eofReader bytes.Buffer 338 | _, err = dbms.MSSQLReadPacket(&eofReader) 339 | if err != nil { 340 | t.Error("Got error", err) 341 | } 342 | } 343 | 344 | func BenchmarkMSSQLReadPacket(b *testing.B) { 345 | var buf [1024]byte 346 | for i := 0; i < b.N; i++ { 347 | s := bytes.NewReader(buf[:]) 348 | dbms.MSSQLReadPacket(s) 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /dbshield/dbms/mysql.go: -------------------------------------------------------------------------------- 1 | package dbms 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "errors" 7 | "io" 8 | "net" 9 | "time" 10 | 11 | "github.com/nim4/DBShield/dbshield/logger" 12 | "github.com/nim4/DBShield/dbshield/sql" 13 | ) 14 | 15 | const maxMySQLPayloadLen = 1<<24 - 1 16 | 17 | //MySQL DBMS 18 | type MySQL struct { 19 | client net.Conn 20 | server net.Conn 21 | certificate tls.Certificate 22 | currentDB []byte 23 | username []byte 24 | reader func(io.Reader) ([]byte, error) 25 | } 26 | 27 | //SetCertificate to use if client asks for SSL 28 | func (m *MySQL) SetCertificate(crt, key string) (err error) { 29 | m.certificate, err = tls.LoadX509KeyPair(crt, key) 30 | return 31 | } 32 | 33 | //SetReader function for sockets IO 34 | func (m *MySQL) SetReader(f func(io.Reader) ([]byte, error)) { 35 | m.reader = f 36 | } 37 | 38 | //SetSockets for dbms (client and server sockets) 39 | func (m *MySQL) SetSockets(c, s net.Conn) { 40 | m.client = c 41 | m.server = s 42 | } 43 | 44 | //Close sockets 45 | func (m *MySQL) Close() { 46 | defer handlePanic() 47 | m.client.Close() 48 | m.server.Close() 49 | } 50 | 51 | //DefaultPort of the DBMS 52 | func (m *MySQL) DefaultPort() uint { 53 | return 3306 54 | } 55 | 56 | //Handler gets incoming requests 57 | func (m *MySQL) Handler() error { 58 | defer handlePanic() 59 | defer m.Close() 60 | success, err := m.handleLogin() 61 | if err != nil { 62 | return err 63 | } 64 | if !success { 65 | logger.Warning("Login failed") 66 | return nil 67 | } 68 | for { 69 | var buf []byte 70 | buf, err = ReadPacket(m.client) 71 | if err != nil || len(buf) < 5 { 72 | return err 73 | } 74 | data := buf[4:] 75 | 76 | switch data[0] { 77 | case 0x01: //Quit 78 | return nil 79 | case 0x02: //UseDB 80 | m.currentDB = data[1:] 81 | logger.Debugf("Using database: %v", m.currentDB) 82 | case 0x03: //Query 83 | query := data[1:] 84 | context := sql.QueryContext{ 85 | Query: query, 86 | Database: m.currentDB, 87 | User: m.username, 88 | Client: remoteAddrToIP(m.client.RemoteAddr()), 89 | Time: time.Now(), 90 | } 91 | processContext(context) 92 | // case 0x04: //Show fields 93 | // logger.Debugf("Show fields: %s", data[1:]) 94 | // default: 95 | // logger.Debugf("Unknown Data[0]: %x", data[0]) 96 | } 97 | 98 | //Send query/request to server 99 | _, err = m.server.Write(buf) 100 | if err != nil { 101 | return err 102 | } 103 | //Recive response 104 | err = readWrite(m.server, m.client, m.reader) 105 | if err != nil { 106 | return err 107 | } 108 | } 109 | } 110 | 111 | func (m *MySQL) handleLogin() (success bool, err error) { 112 | //Receive Server Greeting 113 | err = readWrite(m.server, m.client, ReadPacket) 114 | if err != nil { 115 | return 116 | } 117 | 118 | //Receive Login Request 119 | buf, err := ReadPacket(m.client) 120 | if err != nil { 121 | return 122 | } 123 | data := buf[4:] 124 | 125 | m.username, m.currentDB = MySQLGetUsernameDB(data) 126 | 127 | //check if ssl is required 128 | ssl := (data[1] & 0x08) == 0x08 129 | 130 | //Send Login Request 131 | _, err = m.server.Write(buf) 132 | if err != nil { 133 | return 134 | } 135 | if ssl { 136 | m.client, m.server, err = turnSSL(m.client, m.server, m.certificate) 137 | if err != nil { 138 | return 139 | } 140 | buf, err = ReadPacket(m.client) 141 | if err != nil { 142 | return 143 | } 144 | data = buf[4:] 145 | m.username, m.currentDB = MySQLGetUsernameDB(data) 146 | 147 | //Send Login Request 148 | _, err = m.server.Write(buf) 149 | if err != nil { 150 | return 151 | } 152 | } 153 | logger.Debugf("SSL bit: %v", ssl) 154 | 155 | if len(m.currentDB) != 0 { //db Selected 156 | //Receive OK 157 | buf, err = ReadPacket(m.server) 158 | if err != nil { 159 | return 160 | } 161 | } else { 162 | //Receive Auth Switch Request 163 | err = readWrite(m.server, m.client, ReadPacket) 164 | if err != nil { 165 | return 166 | } 167 | //Receive Auth Switch Response 168 | err = readWrite(m.client, m.server, ReadPacket) 169 | if err != nil { 170 | return 171 | } 172 | //Receive Response Status 173 | buf, err = ReadPacket(m.server) 174 | if err != nil { 175 | return 176 | } 177 | } 178 | 179 | if buf[5] != 0x15 { 180 | success = true 181 | } 182 | 183 | //Send Response Status 184 | _, err = m.client.Write(buf) 185 | return 186 | } 187 | 188 | //MySQLReadPacket handles reading mysql packets 189 | func MySQLReadPacket(src io.Reader) ([]byte, error) { 190 | data := make([]byte, maxMySQLPayloadLen) 191 | var prevData []byte 192 | for { 193 | 194 | n, err := src.Read(data) 195 | if err != nil { 196 | return nil, err 197 | } 198 | data = data[:n] 199 | pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16) 200 | 201 | if pktLen == 0 { 202 | if prevData == nil { 203 | return nil, errors.New("Malform Packet") 204 | } 205 | 206 | return prevData, nil 207 | } 208 | 209 | eof := true 210 | if len(data) > 8 { 211 | tail := data[len(data)-9:] 212 | eof = tail[0] == 5 && tail[1] == 0 && tail[2] == 0 && tail[4] == 0xfe 213 | } 214 | 215 | if eof { 216 | if prevData == nil { 217 | return data, nil 218 | } 219 | 220 | return append(prevData, data...), nil 221 | } 222 | 223 | prevData = append(prevData, data...) 224 | } 225 | } 226 | 227 | //MySQLGetUsernameDB parse packet and gets username and db name 228 | func MySQLGetUsernameDB(data []byte) (username, db []byte) { 229 | if len(data) < 33 { 230 | return 231 | } 232 | pos := 32 233 | 234 | nullByteIndex := bytes.IndexByte(data[pos:], 0x00) 235 | username = data[pos : nullByteIndex+pos] 236 | logger.Debugf("Username: %s", username) 237 | pos += nullByteIndex + 22 238 | nullByteIndex = bytes.IndexByte(data[pos:], 0x00) 239 | 240 | //Check if DB name is selected 241 | dbSelectedCheck := len(data) > nullByteIndex+pos+1 242 | 243 | if nullByteIndex != 0 && dbSelectedCheck { 244 | db = data[pos : nullByteIndex+pos] 245 | logger.Debugf("Database: %s", db) 246 | } 247 | return 248 | } 249 | -------------------------------------------------------------------------------- /dbshield/dbms/oracle.go: -------------------------------------------------------------------------------- 1 | package dbms 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "io" 7 | "net" 8 | "time" 9 | 10 | "github.com/nim4/DBShield/dbshield/logger" 11 | "github.com/nim4/DBShield/dbshield/sql" 12 | ) 13 | 14 | //Oracle DBMS 15 | type Oracle struct { 16 | client net.Conn 17 | server net.Conn 18 | certificate tls.Certificate 19 | currentDB []byte 20 | username []byte 21 | reader func(io.Reader) ([]byte, error) 22 | } 23 | 24 | //SetCertificate to use if client asks for SSL 25 | func (o *Oracle) SetCertificate(crt, key string) (err error) { 26 | o.certificate, err = tls.LoadX509KeyPair(crt, key) 27 | return 28 | } 29 | 30 | //SetReader function for sockets IO 31 | func (o *Oracle) SetReader(f func(io.Reader) ([]byte, error)) { 32 | o.reader = f 33 | } 34 | 35 | //SetSockets for dbms (client and server sockets) 36 | func (o *Oracle) SetSockets(c, s net.Conn) { 37 | defer handlePanic() 38 | o.client = c 39 | o.server = s 40 | } 41 | 42 | //Close sockets 43 | func (o *Oracle) Close() { 44 | defer handlePanic() 45 | o.client.Close() 46 | o.server.Close() 47 | } 48 | 49 | //DefaultPort of the DBMS 50 | func (o *Oracle) DefaultPort() uint { 51 | return 1521 52 | } 53 | 54 | //Handler gets incoming requests 55 | func (o *Oracle) Handler() error { 56 | defer handlePanic() 57 | defer o.Close() 58 | 59 | for { 60 | buf, err := o.readPacket(o.client) 61 | if err != nil { 62 | return err 63 | } 64 | var eof bool 65 | 66 | switch buf[4] { //Packet Type 67 | case 0x01: //Connect 68 | connectDataLen := int(buf[24])*256 + int(buf[25]) 69 | connectData := buf[len(buf)-connectDataLen:] 70 | 71 | //Extracting Service name 72 | // FIXME: avoid string 73 | tmp1 := bytes.Split(connectData, []byte("SERVICE_NAME=")) 74 | tmp2 := bytes.Split(tmp1[1], []byte{0x29}) // ) 75 | o.currentDB = tmp2[0] 76 | 77 | logger.Debugf("Connect Data: %s", connectData) 78 | logger.Debugf("Service Name: %s", o.currentDB) 79 | case 0x06: //Data 80 | data := buf[8:] 81 | eof = data[1] == 0x40 82 | if !eof { 83 | payload := data[2:] 84 | if len(payload) > 16 && payload[0] == 0x11 && payload[15] == 0x03 && payload[16] == 0x5e { 85 | // I have no idea what this TTC is but its on top of query 86 | //simply skiping it 87 | payload = payload[15:] 88 | } 89 | switch payload[0] { 90 | case 0x03: 91 | switch payload[1] { 92 | case 0x5e: //reading query 93 | query, _ := pascalString(payload[70:]) 94 | context := sql.QueryContext{ 95 | Query: query, 96 | Database: o.currentDB, 97 | User: o.username, 98 | Client: remoteAddrToIP(o.client.RemoteAddr()), 99 | Time: time.Now(), 100 | } 101 | processContext(context) 102 | case 0x76: // Reading username 103 | val, _ := pascalString(payload[19:]) 104 | o.username = val 105 | logger.Debugf("Username: %s", o.username) 106 | } 107 | } 108 | } 109 | } 110 | 111 | _, err = o.server.Write(buf) 112 | if err != nil || eof { 113 | return err 114 | } 115 | 116 | err = readWrite(o.server, o.client, o.readPacket) 117 | if err != nil { 118 | return err 119 | } 120 | } 121 | } 122 | 123 | //wrapper around our classic readPacket to handle segmented packets 124 | func (o *Oracle) readPacket(c io.Reader) (buf []byte, err error) { 125 | buf, err = o.reader(c) 126 | if err != nil { 127 | return 128 | } 129 | packetLen := int(buf[0])*256 + int(buf[1]) 130 | for { 131 | if len(buf) == packetLen { 132 | break 133 | } 134 | var b []byte 135 | b, err = o.reader(c) 136 | if err != nil { 137 | return 138 | } 139 | buf = append(buf, b...) 140 | } 141 | return 142 | } 143 | -------------------------------------------------------------------------------- /dbshield/dbms/postgres.go: -------------------------------------------------------------------------------- 1 | package dbms 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "encoding/binary" 7 | "io" 8 | "net" 9 | "strings" 10 | "time" 11 | 12 | "github.com/nim4/DBShield/dbshield/logger" 13 | "github.com/nim4/DBShield/dbshield/sql" 14 | ) 15 | 16 | //Postgres DBMS 17 | type Postgres struct { 18 | client net.Conn 19 | server net.Conn 20 | certificate tls.Certificate 21 | currentDB []byte 22 | username []byte 23 | reader func(io.Reader) ([]byte, error) 24 | } 25 | 26 | //SetCertificate to use if client asks for SSL 27 | func (p *Postgres) SetCertificate(crt, key string) (err error) { 28 | p.certificate, err = tls.LoadX509KeyPair(crt, key) 29 | return 30 | } 31 | 32 | //SetReader function for sockets IO 33 | func (p *Postgres) SetReader(f func(io.Reader) ([]byte, error)) { 34 | p.reader = f 35 | } 36 | 37 | //SetSockets for dbms (client and server sockets) 38 | func (p *Postgres) SetSockets(c, s net.Conn) { 39 | defer handlePanic() 40 | p.client = c 41 | p.server = s 42 | } 43 | 44 | //Close sockets 45 | func (p *Postgres) Close() { 46 | defer handlePanic() 47 | p.client.Close() 48 | p.server.Close() 49 | } 50 | 51 | //DefaultPort of the DBMS 52 | func (p *Postgres) DefaultPort() uint { 53 | return 5432 54 | } 55 | 56 | //Handler gets incoming requests 57 | func (p *Postgres) Handler() (err error) { 58 | //defer handlePanic() 59 | defer p.Close() 60 | 61 | success, err := p.handleLogin() 62 | if err != nil { 63 | return 64 | } 65 | if !success { 66 | logger.Warning("Login failed") 67 | return 68 | } 69 | for { 70 | var buf []byte 71 | //Read client request 72 | buf, err = p.reader(p.client) 73 | if err != nil { 74 | return 75 | } 76 | switch buf[0] { 77 | case 0x51: //Simple query 78 | context := sql.QueryContext{ 79 | Query: buf[5:], 80 | Database: p.currentDB, 81 | User: p.username, 82 | Client: remoteAddrToIP(p.client.RemoteAddr()), 83 | Time: time.Now(), 84 | } 85 | processContext(context) 86 | 87 | case 0x58: //Terminate 88 | _, err = p.server.Write(buf) 89 | return 90 | } 91 | 92 | //Send request to server 93 | _, err = p.server.Write(buf) 94 | if err != nil { 95 | return 96 | } 97 | 98 | //Read server response 99 | buf, err = p.reader(p.server) 100 | if err != nil { 101 | return 102 | } 103 | 104 | //Send response to client 105 | _, err = p.client.Write(buf) 106 | if err != nil { 107 | return 108 | } 109 | 110 | switch buf[0] { 111 | case 0x45: //Error 112 | buf, err = p.reader(p.server) 113 | if err != nil { 114 | return 115 | } 116 | _, err = p.client.Write(buf) 117 | if err != nil { 118 | return 119 | } 120 | } 121 | } 122 | } 123 | 124 | func (p *Postgres) handleLogin() (success bool, err error) { 125 | //Receive Greeting 126 | err = readWrite(p.client, p.server, p.reader) 127 | if err != nil { 128 | return 129 | } 130 | 131 | //Receive Greeting 132 | buf, err := p.reader(p.server) 133 | if err != nil { 134 | return 135 | } 136 | ssl := buf[0] == 0x53 137 | 138 | //Send Greeting 139 | _, err = p.client.Write(buf) 140 | if err != nil { 141 | return 142 | } 143 | 144 | if ssl { 145 | p.client, p.server, err = turnSSL(p.client, p.server, p.certificate) 146 | if err != nil { 147 | return 148 | } 149 | } 150 | 151 | //Receive username and database name 152 | buf, err = p.reader(p.client) 153 | if err != nil { 154 | return 155 | } 156 | 157 | data := buf[8:] 158 | 159 | payload := make(map[string][]byte) 160 | for { 161 | //reading key 162 | nullByteIndex := bytes.IndexByte(data, 0x00) 163 | if nullByteIndex <= 0 { 164 | break 165 | } 166 | key := string(data[:nullByteIndex+1]) 167 | 168 | //reading value 169 | data = data[nullByteIndex+1:] 170 | nullByteIndex = bytes.IndexByte(data, 0x00) 171 | if nullByteIndex <= 0 { 172 | break 173 | } 174 | payload[key] = data[:nullByteIndex+1] 175 | data = data[nullByteIndex+1:] 176 | } 177 | for key := range payload { 178 | logger.Debugf("%s: %s", strings.Title(key), payload[key]) 179 | } 180 | p.username = payload["user"] 181 | p.currentDB = payload["database"] 182 | 183 | //Send username & dbname to server 184 | _, err = p.server.Write(buf) 185 | if err != nil { 186 | return 187 | } 188 | 189 | //Read authentication request from server 190 | err = readWrite(p.server, p.client, p.reader) 191 | if err != nil { 192 | return 193 | } 194 | 195 | //Read client password message 196 | err = readWrite(p.client, p.server, p.reader) 197 | if err != nil { 198 | return 199 | } 200 | 201 | //Read authtentication result from server 202 | buf, err = p.reader(p.server) 203 | if err != nil { 204 | return 205 | } 206 | data = buf[5:9] 207 | if binary.BigEndian.Uint32(data) == 0 { 208 | success = true 209 | } 210 | //Send authtentication result to client 211 | _, err = p.client.Write(buf) 212 | return 213 | } 214 | -------------------------------------------------------------------------------- /dbshield/dbms/postgres_test.go: -------------------------------------------------------------------------------- 1 | package dbms_test 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "testing" 7 | 8 | "github.com/nim4/DBShield/dbshield/dbms" 9 | "github.com/nim4/mock" 10 | ) 11 | 12 | var samplePostgres = [...][]byte{ 13 | {0x00, 0x00, 0x00, 0x08, 0x04, 0xd2, 0x16, 0x2f}, //Client 14 | {0x4e}, 15 | { 16 | 0x00, 0x00, 0x00, 0x25, 0x00, 0x03, 0x00, 0x00, 0x75, 0x73, 0x65, 0x72, 17 | 0x00, 0x70, 0x6f, 0x73, 0x74, 0x67, 0x72, 0x65, 0x73, 0x00, 0x64, 0x61, 18 | 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x00, 0x74, 0x65, 0x73, 0x74, 0x00, 19 | 0x00, 20 | }, 21 | { 22 | 0x52, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x05, 0x15, 0x81, 0xd2, 23 | 0xfb, 24 | }, 25 | { 26 | 0x70, 0x00, 0x00, 0x00, 0x28, 0x6d, 0x64, 0x35, 0x61, 0x65, 0x66, 0x34, 27 | 0x36, 0x64, 0x61, 0x32, 0x30, 0x31, 0x31, 0x37, 0x63, 0x61, 0x35, 0x30, 28 | 0x65, 0x37, 0x34, 0x64, 0x33, 0x64, 0x66, 0x61, 0x65, 0x31, 0x65, 0x62, 29 | 0x33, 0x37, 0x35, 0x32, 0x00, 30 | }, 31 | {0x52, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00}, 32 | { 33 | 0x51, 0x00, 0x00, 0x00, 0x19, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 0x20, 34 | 0x2a, 0x20, 0x46, 0x52, 0x4f, 0x4d, 0x20, 0x73, 0x74, 0x6f, 0x63, 0x6b, 35 | 0x73, 0x00, 36 | }, 37 | { 38 | 0x54, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x03, 0x69, 0x64, 0x00, 0x00, 0x00, 39 | 0x40, 0x18, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0xff, 0xff, 40 | 0xff, 0xff, 0x00, 0x00, 0x73, 0x79, 0x6d, 0x62, 0x6f, 0x6c, 0x00, 0x00, 41 | 0x00, 0x40, 0x18, 0x00, 0x02, 0x00, 0x00, 0x04, 0x13, 0xff, 0xff, 0x00, 42 | 0x00, 0x00, 0x0e, 0x00, 0x00, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x79, 43 | 0x00, 0x00, 0x00, 0x40, 0x18, 0x00, 0x03, 0x00, 0x00, 0x04, 0x13, 0xff, 44 | 0xff, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x19, 45 | 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x31, 0x00, 0x00, 0x00, 0x01, 0x50, 46 | 0x00, 0x00, 0x00, 0x05, 0x50, 0x61, 0x72, 0x74, 0x61, 0x44, 0x00, 0x00, 47 | 0x00, 0x19, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x32, 0x00, 0x00, 0x00, 48 | 0x01, 0x58, 0x00, 0x00, 0x00, 0x05, 0x58, 0x61, 0x72, 0x74, 0x61, 0x44, 49 | 0x00, 0x00, 0x00, 0x19, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x33, 0x00, 50 | 0x00, 0x00, 0x01, 0x5a, 0x00, 0x00, 0x00, 0x05, 0x5a, 0x61, 0x72, 0x74, 51 | 0x61, 0x43, 0x00, 0x00, 0x00, 0x0d, 0x53, 0x45, 0x4c, 0x45, 0x43, 0x54, 52 | 0x20, 0x33, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x05, 0x49, 53 | }, 54 | {0x58, 0x00, 0x00, 0x00, 0x04}, 55 | } 56 | 57 | var postgresCount int 58 | 59 | func postgresDummyReader(c io.Reader) (buf []byte, err error) { 60 | if postgresCount < len(samplePostgres) { 61 | buf = samplePostgres[postgresCount] 62 | postgresCount++ 63 | } else { 64 | err = errors.New("EOF") 65 | } 66 | return 67 | } 68 | 69 | func TestPostgres(t *testing.T) { 70 | p := new(dbms.Postgres) 71 | port := p.DefaultPort() 72 | if p.DefaultPort() != 5432 { 73 | t.Error("Expected 5432, got ", port) 74 | } 75 | err := p.SetCertificate("", "") 76 | if err == nil { 77 | t.Error("Expected error") 78 | } 79 | p.SetReader(postgresDummyReader) 80 | var s mock.ConnMock 81 | p.SetSockets(&s, &s) 82 | err = p.Handler() 83 | if err != nil { 84 | t.Error("Got error", err) 85 | } 86 | p.Close() 87 | } 88 | 89 | func BenchmarkPostgres(b *testing.B) { 90 | b.ReportAllocs() 91 | b.ResetTimer() 92 | var s mock.ConnMock 93 | var p = dbms.Postgres{} 94 | p.SetReader(postgresDummyReader) 95 | p.SetSockets(&s, &s) 96 | for i := 0; i < b.N; i++ { 97 | postgresCount = 0 98 | err := p.Handler() 99 | if err != nil { 100 | b.Fatal(err) 101 | } 102 | p.Close() 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /dbshield/dbms/utils.go: -------------------------------------------------------------------------------- 1 | package dbms 2 | 3 | import ( 4 | "crypto/tls" 5 | "net" 6 | 7 | "github.com/nim4/DBShield/dbshield/config" 8 | "github.com/nim4/DBShield/dbshield/logger" 9 | "github.com/nim4/DBShield/dbshield/sql" 10 | "github.com/nim4/DBShield/dbshield/training" 11 | ) 12 | 13 | var decodingTable = [...]byte{ 14 | '\x00', // 0x00 -> NULL 15 | '\x01', // 0x01 -> START OF HEADING 16 | '\x02', // 0x02 -> START OF TEXT 17 | '\x03', // 0x03 -> END OF TEXT 18 | '\x9c', // 0x04 -> CONTROL 19 | '\t', // 0x05 -> HORIZONTAL TABULATION 20 | '\x86', // 0x06 -> CONTROL 21 | '\x7f', // 0x07 -> DELETE 22 | '\x97', // 0x08 -> CONTROL 23 | '\x8d', // 0x09 -> CONTROL 24 | '\x8e', // 0x0A -> CONTROL 25 | '\x0b', // 0x0B -> VERTICAL TABULATION 26 | '\x0c', // 0x0C -> FORM FEED 27 | '\r', // 0x0D -> CARRIAGE RETURN 28 | '\x0e', // 0x0E -> SHIFT OUT 29 | '\x0f', // 0x0F -> SHIFT IN 30 | '\x10', // 0x10 -> DATA LINK ESCAPE 31 | '\x11', // 0x11 -> DEVICE CONTROL ONE 32 | '\x12', // 0x12 -> DEVICE CONTROL TWO 33 | '\x13', // 0x13 -> DEVICE CONTROL THREE 34 | '\x9d', // 0x14 -> CONTROL 35 | '\x85', // 0x15 -> CONTROL 36 | '\x08', // 0x16 -> BACKSPACE 37 | '\x87', // 0x17 -> CONTROL 38 | '\x18', // 0x18 -> CANCEL 39 | '\x19', // 0x19 -> END OF MEDIUM 40 | '\x92', // 0x1A -> CONTROL 41 | '\x8f', // 0x1B -> CONTROL 42 | '\x1c', // 0x1C -> FILE SEPARATOR 43 | '\x1d', // 0x1D -> GROUP SEPARATOR 44 | '\x1e', // 0x1E -> RECORD SEPARATOR 45 | '\x1f', // 0x1F -> UNIT SEPARATOR 46 | '\x80', // 0x20 -> CONTROL 47 | '\x81', // 0x21 -> CONTROL 48 | '\x82', // 0x22 -> CONTROL 49 | '\x83', // 0x23 -> CONTROL 50 | '\x84', // 0x24 -> CONTROL 51 | '\n', // 0x25 -> LINE FEED 52 | '\x17', // 0x26 -> END OF TRANSMISSION BLOCK 53 | '\x1b', // 0x27 -> ESCAPE 54 | '\x88', // 0x28 -> CONTROL 55 | '\x89', // 0x29 -> CONTROL 56 | '\x8a', // 0x2A -> CONTROL 57 | '\x8b', // 0x2B -> CONTROL 58 | '\x8c', // 0x2C -> CONTROL 59 | '\x05', // 0x2D -> ENQUIRY 60 | '\x06', // 0x2E -> ACKNOWLEDGE 61 | '\x07', // 0x2F -> BELL 62 | '\x90', // 0x30 -> CONTROL 63 | '\x91', // 0x31 -> CONTROL 64 | '\x16', // 0x32 -> SYNCHRONOUS IDLE 65 | '\x93', // 0x33 -> CONTROL 66 | '\x94', // 0x34 -> CONTROL 67 | '\x95', // 0x35 -> CONTROL 68 | '\x96', // 0x36 -> CONTROL 69 | '\x04', // 0x37 -> END OF TRANSMISSION 70 | '\x98', // 0x38 -> CONTROL 71 | '\x99', // 0x39 -> CONTROL 72 | '\x9a', // 0x3A -> CONTROL 73 | '\x9b', // 0x3B -> CONTROL 74 | '\x14', // 0x3C -> DEVICE CONTROL FOUR 75 | '\x15', // 0x3D -> NEGATIVE ACKNOWLEDGE 76 | '\x9e', // 0x3E -> CONTROL 77 | '\x1a', // 0x3F -> SUBSTITUTE 78 | ' ', // 0x40 -> SPACE 79 | '\xa0', // 0x41 -> NO-BREAK SPACE 80 | '\xe2', // 0x42 -> LATIN SMALL LETTER A WITH CIRCUMFLEX 81 | '\xe4', // 0x43 -> LATIN SMALL LETTER A WITH DIAERESIS 82 | '\xe0', // 0x44 -> LATIN SMALL LETTER A WITH GRAVE 83 | '\xe1', // 0x45 -> LATIN SMALL LETTER A WITH ACUTE 84 | '\xe3', // 0x46 -> LATIN SMALL LETTER A WITH TILDE 85 | '\xe5', // 0x47 -> LATIN SMALL LETTER A WITH RING ABOVE 86 | '\xe7', // 0x48 -> LATIN SMALL LETTER C WITH CEDILLA 87 | '\xf1', // 0x49 -> LATIN SMALL LETTER N WITH TILDE 88 | '[', // 0x4A -> LEFT SQUARE BRACKET 89 | '.', // 0x4B -> FULL STOP 90 | '<', // 0x4C -> LESS-THAN SIGN 91 | '(', // 0x4D -> LEFT PARENTHESIS 92 | '+', // 0x4E -> PLUS SIGN 93 | '!', // 0x4F -> EXCLAMATION MARK 94 | '&', // 0x50 -> AMPERSAND 95 | '\xe9', // 0x51 -> LATIN SMALL LETTER E WITH ACUTE 96 | '\xea', // 0x52 -> LATIN SMALL LETTER E WITH CIRCUMFLEX 97 | '\xeb', // 0x53 -> LATIN SMALL LETTER E WITH DIAERESIS 98 | '\xe8', // 0x54 -> LATIN SMALL LETTER E WITH GRAVE 99 | '\xed', // 0x55 -> LATIN SMALL LETTER I WITH ACUTE 100 | '\xee', // 0x56 -> LATIN SMALL LETTER I WITH CIRCUMFLEX 101 | '\xef', // 0x57 -> LATIN SMALL LETTER I WITH DIAERESIS 102 | '\xec', // 0x58 -> LATIN SMALL LETTER I WITH GRAVE 103 | '\xdf', // 0x59 -> LATIN SMALL LETTER SHARP S (GERMAN) 104 | ']', // 0x5A -> RIGHT SQUARE BRACKET 105 | '$', // 0x5B -> DOLLAR SIGN 106 | '*', // 0x5C -> ASTERISK 107 | ')', // 0x5D -> RIGHT PARENTHESIS 108 | ';', // 0x5E -> SEMICOLON 109 | '^', // 0x5F -> CIRCUMFLEX ACCENT 110 | '-', // 0x60 -> HYPHEN-MINUS 111 | '/', // 0x61 -> SOLIDUS 112 | '\xc2', // 0x62 -> LATIN CAPITAL LETTER A WITH CIRCUMFLEX 113 | '\xc4', // 0x63 -> LATIN CAPITAL LETTER A WITH DIAERESIS 114 | '\xc0', // 0x64 -> LATIN CAPITAL LETTER A WITH GRAVE 115 | '\xc1', // 0x65 -> LATIN CAPITAL LETTER A WITH ACUTE 116 | '\xc3', // 0x66 -> LATIN CAPITAL LETTER A WITH TILDE 117 | '\xc5', // 0x67 -> LATIN CAPITAL LETTER A WITH RING ABOVE 118 | '\xc7', // 0x68 -> LATIN CAPITAL LETTER C WITH CEDILLA 119 | '\xd1', // 0x69 -> LATIN CAPITAL LETTER N WITH TILDE 120 | '\xa6', // 0x6A -> BROKEN BAR 121 | ',', // 0x6B -> COMMA 122 | '%', // 0x6C -> PERCENT SIGN 123 | '_', // 0x6D -> LOW LINE 124 | '>', // 0x6E -> GREATER-THAN SIGN 125 | '?', // 0x6F -> QUESTION MARK 126 | '\xf8', // 0x70 -> LATIN SMALL LETTER O WITH STROKE 127 | '\xc9', // 0x71 -> LATIN CAPITAL LETTER E WITH ACUTE 128 | '\xca', // 0x72 -> LATIN CAPITAL LETTER E WITH CIRCUMFLEX 129 | '\xcb', // 0x73 -> LATIN CAPITAL LETTER E WITH DIAERESIS 130 | '\xc8', // 0x74 -> LATIN CAPITAL LETTER E WITH GRAVE 131 | '\xcd', // 0x75 -> LATIN CAPITAL LETTER I WITH ACUTE 132 | '\xce', // 0x76 -> LATIN CAPITAL LETTER I WITH CIRCUMFLEX 133 | '\xcf', // 0x77 -> LATIN CAPITAL LETTER I WITH DIAERESIS 134 | '\xcc', // 0x78 -> LATIN CAPITAL LETTER I WITH GRAVE 135 | '`', // 0x79 -> GRAVE ACCENT 136 | ':', // 0x7A -> COLON 137 | '#', // 0x7B -> NUMBER SIGN 138 | '@', // 0x7C -> COMMERCIAL AT 139 | '\'', // 0x7D -> APOSTROPHE 140 | '=', // 0x7E -> EQUALS SIGN 141 | '"', // 0x7F -> QUOTATION MARK 142 | '\xd8', // 0x80 -> LATIN CAPITAL LETTER O WITH STROKE 143 | 'a', // 0x81 -> LATIN SMALL LETTER A 144 | 'b', // 0x82 -> LATIN SMALL LETTER B 145 | 'c', // 0x83 -> LATIN SMALL LETTER C 146 | 'd', // 0x84 -> LATIN SMALL LETTER D 147 | 'e', // 0x85 -> LATIN SMALL LETTER E 148 | 'f', // 0x86 -> LATIN SMALL LETTER F 149 | 'g', // 0x87 -> LATIN SMALL LETTER G 150 | 'h', // 0x88 -> LATIN SMALL LETTER H 151 | 'i', // 0x89 -> LATIN SMALL LETTER I 152 | '\xab', // 0x8A -> LEFT-POINTING DOUBLE ANGLE QUOTATION MARK 153 | '\xbb', // 0x8B -> RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK 154 | '\xf0', // 0x8C -> LATIN SMALL LETTER ETH (ICELANDIC) 155 | '\xfd', // 0x8D -> LATIN SMALL LETTER Y WITH ACUTE 156 | '\xfe', // 0x8E -> LATIN SMALL LETTER THORN (ICELANDIC) 157 | '\xb1', // 0x8F -> PLUS-MINUS SIGN 158 | '\xb0', // 0x90 -> DEGREE SIGN 159 | 'j', // 0x91 -> LATIN SMALL LETTER J 160 | 'k', // 0x92 -> LATIN SMALL LETTER K 161 | 'l', // 0x93 -> LATIN SMALL LETTER L 162 | 'm', // 0x94 -> LATIN SMALL LETTER M 163 | 'n', // 0x95 -> LATIN SMALL LETTER N 164 | 'o', // 0x96 -> LATIN SMALL LETTER O 165 | 'p', // 0x97 -> LATIN SMALL LETTER P 166 | 'q', // 0x98 -> LATIN SMALL LETTER Q 167 | 'r', // 0x99 -> LATIN SMALL LETTER R 168 | '\xaa', // 0x9A -> FEMININE ORDINAL INDICATOR 169 | '\xba', // 0x9B -> MASCULINE ORDINAL INDICATOR 170 | '\xe6', // 0x9C -> LATIN SMALL LIGATURE AE 171 | '\xb8', // 0x9D -> CEDILLA 172 | '\xc6', // 0x9E -> LATIN CAPITAL LIGATURE AE 173 | '\xa4', // 0x9F -> CURRENCY SIGN 174 | '\xb5', // 0xA0 -> MICRO SIGN 175 | '~', // 0xA1 -> TILDE 176 | 's', // 0xA2 -> LATIN SMALL LETTER S 177 | 't', // 0xA3 -> LATIN SMALL LETTER T 178 | 'u', // 0xA4 -> LATIN SMALL LETTER U 179 | 'v', // 0xA5 -> LATIN SMALL LETTER V 180 | 'w', // 0xA6 -> LATIN SMALL LETTER W 181 | 'x', // 0xA7 -> LATIN SMALL LETTER X 182 | 'y', // 0xA8 -> LATIN SMALL LETTER Y 183 | 'z', // 0xA9 -> LATIN SMALL LETTER Z 184 | '\xa1', // 0xAA -> INVERTED EXCLAMATION MARK 185 | '\xbf', // 0xAB -> INVERTED QUESTION MARK 186 | '\xd0', // 0xAC -> LATIN CAPITAL LETTER ETH (ICELANDIC) 187 | '\xdd', // 0xAD -> LATIN CAPITAL LETTER Y WITH ACUTE 188 | '\xde', // 0xAE -> LATIN CAPITAL LETTER THORN (ICELANDIC) 189 | '\xae', // 0xAF -> REGISTERED SIGN 190 | '\xa2', // 0xB0 -> CENT SIGN 191 | '\xa3', // 0xB1 -> POUND SIGN 192 | '\xa5', // 0xB2 -> YEN SIGN 193 | '\xb7', // 0xB3 -> MIDDLE DOT 194 | '\xa9', // 0xB4 -> COPYRIGHT SIGN 195 | '\xa7', // 0xB5 -> SECTION SIGN 196 | '\xb6', // 0xB6 -> PILCROW SIGN 197 | '\xbc', // 0xB7 -> VULGAR FRACTION ONE QUARTER 198 | '\xbd', // 0xB8 -> VULGAR FRACTION ONE HALF 199 | '\xbe', // 0xB9 -> VULGAR FRACTION THREE QUARTERS 200 | '\xac', // 0xBA -> NOT SIGN 201 | '|', // 0xBB -> VERTICAL LINE 202 | '\xaf', // 0xBC -> MACRON 203 | '\xa8', // 0xBD -> DIAERESIS 204 | '\xb4', // 0xBE -> ACUTE ACCENT 205 | '\xd7', // 0xBF -> MULTIPLICATION SIGN 206 | '{', // 0xC0 -> LEFT CURLY BRACKET 207 | 'A', // 0xC1 -> LATIN CAPITAL LETTER A 208 | 'B', // 0xC2 -> LATIN CAPITAL LETTER B 209 | 'C', // 0xC3 -> LATIN CAPITAL LETTER C 210 | 'D', // 0xC4 -> LATIN CAPITAL LETTER D 211 | 'E', // 0xC5 -> LATIN CAPITAL LETTER E 212 | 'F', // 0xC6 -> LATIN CAPITAL LETTER F 213 | 'G', // 0xC7 -> LATIN CAPITAL LETTER G 214 | 'H', // 0xC8 -> LATIN CAPITAL LETTER H 215 | 'I', // 0xC9 -> LATIN CAPITAL LETTER I 216 | '\xad', // 0xCA -> SOFT HYPHEN 217 | '\xf4', // 0xCB -> LATIN SMALL LETTER O WITH CIRCUMFLEX 218 | '\xf6', // 0xCC -> LATIN SMALL LETTER O WITH DIAERESIS 219 | '\xf2', // 0xCD -> LATIN SMALL LETTER O WITH GRAVE 220 | '\xf3', // 0xCE -> LATIN SMALL LETTER O WITH ACUTE 221 | '\xf5', // 0xCF -> LATIN SMALL LETTER O WITH TILDE 222 | '}', // 0xD0 -> RIGHT CURLY BRACKET 223 | 'J', // 0xD1 -> LATIN CAPITAL LETTER J 224 | 'K', // 0xD2 -> LATIN CAPITAL LETTER K 225 | 'L', // 0xD3 -> LATIN CAPITAL LETTER L 226 | 'M', // 0xD4 -> LATIN CAPITAL LETTER M 227 | 'N', // 0xD5 -> LATIN CAPITAL LETTER N 228 | 'O', // 0xD6 -> LATIN CAPITAL LETTER O 229 | 'P', // 0xD7 -> LATIN CAPITAL LETTER P 230 | 'Q', // 0xD8 -> LATIN CAPITAL LETTER Q 231 | 'R', // 0xD9 -> LATIN CAPITAL LETTER R 232 | '\xb9', // 0xDA -> SUPERSCRIPT ONE 233 | '\xfb', // 0xDB -> LATIN SMALL LETTER U WITH CIRCUMFLEX 234 | '\xfc', // 0xDC -> LATIN SMALL LETTER U WITH DIAERESIS 235 | '\xf9', // 0xDD -> LATIN SMALL LETTER U WITH GRAVE 236 | '\xfa', // 0xDE -> LATIN SMALL LETTER U WITH ACUTE 237 | '\xff', // 0xDF -> LATIN SMALL LETTER Y WITH DIAERESIS 238 | '\\', // 0xE0 -> REVERSE SOLIDUS 239 | '\xf7', // 0xE1 -> DIVISION SIGN 240 | 'S', // 0xE2 -> LATIN CAPITAL LETTER S 241 | 'T', // 0xE3 -> LATIN CAPITAL LETTER T 242 | 'U', // 0xE4 -> LATIN CAPITAL LETTER U 243 | 'V', // 0xE5 -> LATIN CAPITAL LETTER V 244 | 'W', // 0xE6 -> LATIN CAPITAL LETTER W 245 | 'X', // 0xE7 -> LATIN CAPITAL LETTER X 246 | 'Y', // 0xE8 -> LATIN CAPITAL LETTER Y 247 | 'Z', // 0xE9 -> LATIN CAPITAL LETTER Z 248 | '\xb2', // 0xEA -> SUPERSCRIPT TWO 249 | '\xd4', // 0xEB -> LATIN CAPITAL LETTER O WITH CIRCUMFLEX 250 | '\xd6', // 0xEC -> LATIN CAPITAL LETTER O WITH DIAERESIS 251 | '\xd2', // 0xED -> LATIN CAPITAL LETTER O WITH GRAVE 252 | '\xd3', // 0xEE -> LATIN CAPITAL LETTER O WITH ACUTE 253 | '\xd5', // 0xEF -> LATIN CAPITAL LETTER O WITH TILDE 254 | '0', // 0xF0 -> DIGIT ZERO 255 | '1', // 0xF1 -> DIGIT ONE 256 | '2', // 0xF2 -> DIGIT TWO 257 | '3', // 0xF3 -> DIGIT THREE 258 | '4', // 0xF4 -> DIGIT FOUR 259 | '5', // 0xF5 -> DIGIT FIVE 260 | '6', // 0xF6 -> DIGIT SIX 261 | '7', // 0xF7 -> DIGIT SEVEN 262 | '8', // 0xF8 -> DIGIT EIGHT 263 | '9', // 0xF9 -> DIGIT NINE 264 | '\xb3', // 0xFA -> SUPERSCRIPT THREE 265 | '\xdb', // 0xFB -> LATIN CAPITAL LETTER U WITH CIRCUMFLEX 266 | '\xdc', // 0xFC -> LATIN CAPITAL LETTER U WITH DIAERESIS 267 | '\xd9', // 0xFD -> LATIN CAPITAL LETTER U WITH GRAVE 268 | '\xda', // 0xFE -> LATIN CAPITAL LETTER U WITH ACUTE 269 | '\x9f', // 0xFF -> CONTROL 270 | } 271 | 272 | func ebc2asc(ebc []byte) []byte { 273 | asc := make([]byte, len(ebc)) 274 | for i, v := range ebc { 275 | asc[i] = decodingTable[v] 276 | } 277 | return asc 278 | } 279 | 280 | func pascalString(data []byte) (b []byte, size uint) { 281 | size = uint(data[0]) 282 | b = data[1 : size+1] 283 | return 284 | } 285 | 286 | func remoteAddrToIP(addr net.Addr) []byte { 287 | return addr.(*net.TCPAddr).IP 288 | } 289 | 290 | func handlePanic() { 291 | if r := recover(); r != nil { 292 | logger.Warningf("%v", r) 293 | } 294 | } 295 | 296 | func threeByteBigEndianToInt(data []byte) uint { 297 | return uint(data[2])*65536 + uint(data[1])*256 + uint(data[0]) 298 | } 299 | 300 | //processContext will handle context depending on running mode 301 | func processContext(context sql.QueryContext) (err error) { 302 | logger.Debugf("Query: %s", context.Query) 303 | 304 | if config.Config.Learning { 305 | return training.AddToTrainingSet(context) 306 | } 307 | if config.Config.ActionFunc != nil && !training.CheckQuery(context) { 308 | return config.Config.ActionFunc() 309 | } 310 | return nil 311 | } 312 | 313 | func turnSSL(client net.Conn, server net.Conn, certificate tls.Certificate) (net.Conn, net.Conn, error) { 314 | logger.Debugf("SSL connection") 315 | tlsConnClient := tls.Server(client, &tls.Config{ 316 | Certificates: []tls.Certificate{certificate}, 317 | InsecureSkipVerify: true, 318 | }) 319 | if err := tlsConnClient.Handshake(); err != nil { 320 | return nil, nil, err 321 | } 322 | logger.Debug("Client handshake done") 323 | 324 | tlsConnServer := tls.Client(server, &tls.Config{ 325 | InsecureSkipVerify: true, 326 | }) 327 | if err := tlsConnServer.Handshake(); err != nil { 328 | return nil, nil, err 329 | } 330 | logger.Debug("Server handshake done") 331 | return tlsConnClient, tlsConnServer, nil 332 | } 333 | -------------------------------------------------------------------------------- /dbshield/dbms/utils_test.go: -------------------------------------------------------------------------------- 1 | package dbms 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "errors" 7 | "io/ioutil" 8 | "log" 9 | "net" 10 | "testing" 11 | "time" 12 | 13 | "github.com/boltdb/bolt" 14 | "github.com/nim4/DBShield/dbshield/config" 15 | "github.com/nim4/DBShield/dbshield/sql" 16 | "github.com/nim4/DBShield/dbshield/training" 17 | "github.com/nim4/mock" 18 | ) 19 | 20 | func TestMain(m *testing.M) { 21 | log.SetOutput(ioutil.Discard) // Avoid log outputs 22 | tmpfile, err := ioutil.TempFile("", "testdb") 23 | if err != nil { 24 | panic(err) 25 | } 26 | defer tmpfile.Close() 27 | path := tmpfile.Name() 28 | training.DBCon, err = bolt.Open(path, 0600, nil) 29 | if err != nil { 30 | panic(err) 31 | } 32 | training.DBCon.Update(func(tx *bolt.Tx) error { 33 | tx.CreateBucket([]byte("pattern")) 34 | tx.CreateBucket([]byte("abnormal")) 35 | tx.CreateBucket([]byte("state")) 36 | return nil 37 | }) 38 | m.Run() 39 | } 40 | 41 | func TestEbc2asc(t *testing.T) { 42 | ret := string(ebc2asc([]byte{0xe2, 0xc1})) 43 | if ret != "SA" { 44 | t.Error("Expected 'SA', got ", ret) 45 | } 46 | } 47 | 48 | func TestPascalString(t *testing.T) { 49 | b, size := pascalString([]byte{0x3, 0x41, 0x41, 0x41}) 50 | if size != 3 { 51 | t.Error("Expected 3, got ", size) 52 | } 53 | if bytes.Compare(b, []byte{0x41, 0x41, 0x41}) != 0 { 54 | t.Error("Expected 'AAA', got ", b) 55 | } 56 | } 57 | 58 | func TestRemoteAddrToIP(t *testing.T) { 59 | addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:80") 60 | ip := remoteAddrToIP(addr) 61 | if bytes.Compare(ip, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 127, 0, 0, 1}) != 0 { 62 | t.Errorf("Expected '127.0.0.1', got %d", ip) 63 | } 64 | 65 | addr, _ = net.ResolveTCPAddr("tcp", "[fe80::ee0e:c4ff:fe22:7105]:80") 66 | ip = remoteAddrToIP(addr) 67 | if bytes.Compare(ip, []byte{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xee, 0x0e, 0xc4, 0xff, 0xfe, 0x22, 0x71, 0x05}) != 0 { 68 | t.Errorf("Expected 'fe80::ee0e:c4ff:fe22', got %x", ip) 69 | } 70 | } 71 | 72 | func BenchmarkRemoteAddrToIP(b *testing.B) { 73 | addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:80") 74 | for i := 0; i < b.N; i++ { 75 | remoteAddrToIP(addr) 76 | } 77 | } 78 | 79 | func TestThreeByteBigEndianToInt(t *testing.T) { 80 | res := threeByteBigEndianToInt([]byte{1, 2, 3}) 81 | if res != 197121 { 82 | t.Error("Expected 197121, got ", res) 83 | } 84 | } 85 | 86 | func TestHandlePanic(t *testing.T) { 87 | defer handlePanic() 88 | panic("Test Panic") 89 | } 90 | 91 | func TestProcessContext(t *testing.T) { 92 | c := sql.QueryContext{ 93 | Query: []byte("select * from test;"), 94 | Database: []byte("test"), 95 | User: []byte("test"), 96 | Client: []byte("127,0,0,1"), 97 | Time: time.Now(), 98 | } 99 | config.Config.Learning = true 100 | processContext(c) 101 | config.Config.Learning = false 102 | config.Config.ActionFunc = func() error { return nil } 103 | processContext(c) 104 | } 105 | 106 | func TestTurnSSL(t *testing.T) { 107 | cert, err := tls.LoadX509KeyPair("../../cert/server-cert.pem", "../../cert/server-key.pem") 108 | if err != nil { 109 | t.Fatal(err) 110 | } 111 | var s = mock.ConnMock{Error: errors.New("Dummy Error")} 112 | _, _, err = turnSSL(&s, &s, cert) 113 | if err == nil { 114 | t.Error("Expected error") 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /dbshield/dbshield.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package dbshield implements the database firewall functionality 3 | */ 4 | package dbshield 5 | 6 | import ( 7 | "encoding/json" 8 | "fmt" 9 | "net" 10 | "os" 11 | "path" 12 | "strconv" 13 | "strings" 14 | "time" 15 | 16 | "github.com/boltdb/bolt" 17 | "github.com/nim4/DBShield/dbshield/config" 18 | "github.com/nim4/DBShield/dbshield/httpserver" 19 | "github.com/nim4/DBShield/dbshield/logger" 20 | "github.com/nim4/DBShield/dbshield/sql" 21 | "github.com/nim4/DBShield/dbshield/training" 22 | ) 23 | 24 | //Version of the library 25 | var Version = "1.0.0-beta4" 26 | 27 | var configFile string 28 | 29 | //SetConfigFile of DBShield 30 | func SetConfigFile(cf string) error { 31 | configFile = cf 32 | err := config.ParseConfig(configFile) 33 | if err != nil { 34 | return err 35 | } 36 | return postConfig() 37 | } 38 | 39 | //ShowConfig writes parsed config file as JSON to STDUT 40 | func ShowConfig() error { 41 | confJSON, err := json.MarshalIndent(config.Config, "", " ") 42 | fmt.Println(string(confJSON)) 43 | return err 44 | } 45 | 46 | //Purge local database 47 | func Purge() error { 48 | return os.Remove(path.Join(config.Config.DBDir, 49 | config.Config.TargetIP+"_"+config.Config.DBType) + ".db") 50 | } 51 | 52 | //Patterns lists the captured patterns 53 | func Patterns() (count int) { 54 | initModel( 55 | path.Join(config.Config.DBDir, 56 | config.Config.TargetIP+"_"+config.Config.DBType) + ".db") 57 | 58 | training.DBCon.View(func(tx *bolt.Tx) error { 59 | b := tx.Bucket([]byte("pattern")) 60 | if b != nil { 61 | return b.ForEach(func(k, v []byte) error { 62 | if strings.Index(string(k), "_client_") == -1 && strings.Index(string(k), "_user_") == -1 { 63 | fmt.Printf( 64 | `-----Pattern: 0x%x 65 | Sample: %s 66 | `, 67 | k, 68 | v, 69 | ) 70 | count++ 71 | } 72 | return nil 73 | }) 74 | } 75 | return nil 76 | }) 77 | return 78 | } 79 | 80 | //Abnormals detected querties 81 | func Abnormals() (count int) { 82 | initModel( 83 | path.Join(config.Config.DBDir, 84 | config.Config.TargetIP+"_"+config.Config.DBType) + ".db") 85 | 86 | training.DBCon.View(func(tx *bolt.Tx) error { 87 | b := tx.Bucket([]byte("abnormal")) 88 | if b != nil { 89 | return b.ForEach(func(k, v []byte) error { 90 | var c sql.QueryContext 91 | c.Unmarshal(v) 92 | fmt.Printf("[%s] [User: %s] [Database: %s] %s\n", 93 | c.Time.Format(time.RFC1123), 94 | c.User, 95 | c.Database, 96 | c.Query) 97 | count++ 98 | return nil 99 | }) 100 | } 101 | return nil 102 | }) 103 | return count 104 | } 105 | 106 | //RemovePattern deletes a pattern from captured patterns DB 107 | func RemovePattern(pattern string) error { 108 | initModel( 109 | path.Join(config.Config.DBDir, 110 | config.Config.TargetIP+"_"+config.Config.DBType) + ".db") 111 | 112 | return training.DBCon.Update(func(tx *bolt.Tx) error { 113 | b := tx.Bucket([]byte("pattern")) 114 | if b != nil { 115 | return b.Delete([]byte(pattern)) 116 | } 117 | return nil 118 | }) 119 | } 120 | 121 | func postConfig() (err error) { 122 | 123 | config.Config.DB, err = dbNameToStruct(config.Config.DBType) 124 | if err != nil { 125 | return err 126 | } 127 | 128 | tmpDBMS, _ := generateDBMS() 129 | if config.Config.ListenPort == 0 { 130 | config.Config.ListenPort = tmpDBMS.DefaultPort() 131 | } 132 | if config.Config.TargetPort == 0 { 133 | config.Config.TargetPort = tmpDBMS.DefaultPort() 134 | } 135 | return 136 | } 137 | 138 | func mainListner() error { 139 | if config.Config.HTTP { 140 | proto := "http" 141 | if config.Config.HTTPSSL { 142 | proto = "https" 143 | } 144 | logger.Infof("Web interface on %s://%s/", proto, config.Config.HTTPAddr) 145 | go httpserver.Serve() 146 | } 147 | serverAddr, _ := net.ResolveTCPAddr("tcp", config.Config.TargetIP+":"+strconv.Itoa(int(config.Config.TargetPort))) 148 | l, err := net.Listen("tcp", config.Config.ListenIP+":"+strconv.Itoa(int(config.Config.ListenPort))) 149 | if err != nil { 150 | return err 151 | } 152 | // Close the listener when the application closes. 153 | defer l.Close() 154 | 155 | for { 156 | // Listen for an incoming connection. 157 | listenConn, err := l.Accept() 158 | if err != nil { 159 | logger.Warningf("Error accepting connection: %v", err) 160 | continue 161 | } 162 | go handleClient(listenConn, serverAddr) 163 | } 164 | } 165 | 166 | //Start the proxy 167 | func Start() (err error) { 168 | initModel( 169 | path.Join(config.Config.DBDir, 170 | config.Config.TargetIP+"_"+config.Config.DBType) + ".db") 171 | 172 | initLogging() 173 | logger.Infof("Config file: %s", configFile) 174 | logger.Infof("Listening: %s:%v", 175 | config.Config.ListenIP, 176 | config.Config.ListenPort) 177 | logger.Infof("Backend: %s (%s:%v)", 178 | config.Config.DBType, 179 | config.Config.TargetIP, 180 | config.Config.TargetPort) 181 | logger.Infof("Protect: %v", !config.Config.Learning) 182 | go mainListner() 183 | signalHandler() 184 | return nil 185 | } 186 | -------------------------------------------------------------------------------- /dbshield/dbshield_test.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package dbshield 4 | 5 | import ( 6 | "fmt" 7 | "io/ioutil" 8 | "net" 9 | "os" 10 | "syscall" 11 | "testing" 12 | "time" 13 | 14 | "github.com/boltdb/bolt" 15 | "github.com/nim4/DBShield/dbshield/config" 16 | "github.com/nim4/DBShield/dbshield/sql" 17 | "github.com/nim4/DBShield/dbshield/training" 18 | ) 19 | 20 | func TestMain(m *testing.M) { 21 | os.Chdir("../") 22 | m.Run() 23 | } 24 | 25 | func TestSetConfigFile(t *testing.T) { 26 | err := SetConfigFile("Invalid.yml") 27 | if err == nil { 28 | t.Error("Expected error") 29 | } 30 | } 31 | 32 | func TestShowConfig(t *testing.T) { 33 | SetConfigFile("conf/dbshield.yml") 34 | err := ShowConfig() 35 | if err != nil { 36 | t.Error("Got error", err) 37 | } 38 | } 39 | 40 | func TestPurge(t *testing.T) { 41 | SetConfigFile("conf/dbshield.yml") 42 | err := Purge() 43 | if err == nil { 44 | t.Error("Expected error") 45 | } 46 | } 47 | 48 | func TestPostConfig(t *testing.T) { 49 | SetConfigFile("conf/dbshield.yml") 50 | config.Config.DBType = "Invalid" 51 | err := postConfig() 52 | if err == nil { 53 | t.Error("Expected error") 54 | } 55 | 56 | config.Config.ListenPort = 0 57 | config.Config.DBType = "mysql" 58 | err = postConfig() 59 | if err != nil { 60 | t.Error("Expected nil got ", err) 61 | } 62 | } 63 | 64 | func TestEveryThing(t *testing.T) { 65 | closeHandlers() 66 | SetConfigFile("conf/dbshield.yml") 67 | //It should fail if port is already open 68 | l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", config.Config.ListenIP, config.Config.ListenPort)) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | defer l.Close() 73 | 74 | err = mainListner() 75 | if err == nil { 76 | t.Error("Expected error") 77 | } 78 | 79 | go func() { 80 | timer := time.NewTimer(time.Second * 2) 81 | <-timer.C 82 | syscall.Kill(syscall.Getpid(), syscall.SIGINT) 83 | }() 84 | err = Start() 85 | if err != nil { 86 | t.Error("Got error", err) 87 | } 88 | file, _ := ioutil.TempFile(os.TempDir(), "tempDB") 89 | defer os.Remove(file.Name()) 90 | training.DBCon, _ = bolt.Open(file.Name(), 0600, nil) 91 | training.DBCon.Update(func(tx *bolt.Tx) error { 92 | tx.CreateBucket([]byte("pattern")) 93 | tx.CreateBucket([]byte("abnormal")) 94 | tx.CreateBucket([]byte("state")) 95 | return nil 96 | }) 97 | query := []byte("select * from test;") 98 | c := sql.QueryContext{ 99 | Query: query, 100 | Database: []byte("test"), 101 | User: []byte("test"), 102 | Client: []byte("127.0.0.1"), 103 | Time: time.Now(), 104 | } 105 | training.CheckQuery(c) 106 | err = training.AddToTrainingSet(c) 107 | if err != nil { 108 | t.Error("Got error", err) 109 | } 110 | pattern := sql.Pattern(query) 111 | 112 | count := Patterns() 113 | if count != 1 { 114 | t.Error("Expected 1 got", count) 115 | } 116 | 117 | count = Abnormals() 118 | if count != 0 { 119 | t.Error("Expected 0 got", count) 120 | } 121 | 122 | err = RemovePattern(string(pattern)) 123 | if err != nil { 124 | t.Error("Expected nil got", err) 125 | } 126 | 127 | count = Patterns() 128 | if count != 0 { 129 | t.Error("Expected 0 got", count) 130 | } 131 | 132 | //Test without bucket 133 | tmpCon := training.DBCon 134 | defer func() { 135 | training.DBCon = tmpCon 136 | }() 137 | tmpfile, err := ioutil.TempFile("", "testdb") 138 | if err != nil { 139 | panic(err) 140 | } 141 | defer tmpfile.Close() 142 | path := tmpfile.Name() 143 | training.DBCon, err = bolt.Open(path, 0600, nil) 144 | if err != nil { 145 | panic(err) 146 | } 147 | 148 | count = Patterns() 149 | if count != 0 { 150 | t.Error("Expected 0 got", count) 151 | } 152 | 153 | count = Abnormals() 154 | if count != 0 { 155 | t.Error("Expected 0 got", count) 156 | } 157 | 158 | err = RemovePattern(string(pattern)) 159 | if err == nil { 160 | t.Error("Expected error got", err) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /dbshield/httpserver/server.go: -------------------------------------------------------------------------------- 1 | package httpserver 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | "os" 7 | "path/filepath" 8 | "runtime" 9 | "sync" 10 | "time" 11 | 12 | "github.com/gorilla/securecookie" 13 | "github.com/nim4/DBShield/dbshield/config" 14 | "github.com/nim4/DBShield/dbshield/logger" 15 | "github.com/nim4/DBShield/dbshield/training" 16 | ) 17 | 18 | var cookieHandler = securecookie.New( 19 | securecookie.GenerateRandomKey(64), 20 | securecookie.GenerateRandomKey(32), 21 | ) 22 | 23 | var singleHTTP sync.Once 24 | var assets = "assets" 25 | 26 | func serve() { 27 | _, serverGoLocation, _, ok := runtime.Caller(0) 28 | if ok { 29 | assets = filepath.Clean(serverGoLocation + "/../../../" + assets) 30 | } 31 | 32 | if fi, err := os.Stat(assets); os.IsNotExist(err) || !fi.Mode().IsDir() { 33 | logger.Warning("Could not find 'assets' directory, Web UI disabled.") 34 | return 35 | } 36 | 37 | http.Handle("/js/", http.FileServer(http.Dir(assets))) 38 | http.Handle("/css/", http.FileServer(http.Dir(assets))) 39 | http.HandleFunc("/", mainHandler) 40 | http.HandleFunc("/report.htm", mainHandler) 41 | http.HandleFunc("/api", apiHandler) 42 | http.HandleFunc("/login", loginHandler) 43 | http.HandleFunc("/logout", logoutHandler) 44 | } 45 | 46 | //Serve HTTP 47 | func Serve() error { 48 | singleHTTP.Do(serve) 49 | if config.Config.HTTPSSL { 50 | return http.ListenAndServeTLS(config.Config.HTTPAddr, config.Config.TLSCertificate, config.Config.TLSPrivateKey, nil) 51 | } 52 | return http.ListenAndServe(config.Config.HTTPAddr, nil) 53 | } 54 | 55 | //mainHandler for html 56 | func mainHandler(w http.ResponseWriter, r *http.Request) { 57 | if checkLogin(r) { 58 | http.ServeFile(w, r, assets+"/report.htm") 59 | return 60 | } 61 | http.ServeFile(w, r, assets+"/index.htm") 62 | } 63 | 64 | //apiHandler returns state 65 | func apiHandler(w http.ResponseWriter, r *http.Request) { 66 | if !checkLogin(r) { 67 | return 68 | } 69 | out, _ := json.Marshal(struct { 70 | Total uint64 71 | Abnormal uint64 72 | }{ 73 | training.QueryCounter, 74 | training.AbnormalCounter, 75 | }) 76 | w.Write(out) 77 | } 78 | 79 | func loginHandler(w http.ResponseWriter, r *http.Request) { 80 | pass := r.FormValue("password") 81 | redirectTarget := "/" 82 | if pass == config.Config.HTTPPassword { 83 | setSession(w) 84 | redirectTarget = "/report.htm" 85 | logger.Warningf("Successful login to web UI [%s] [%s]", r.RemoteAddr, r.UserAgent()) 86 | } else { 87 | logger.Warningf("Failed login to web UI [%s] [%s]", r.RemoteAddr, r.UserAgent()) 88 | } 89 | http.Redirect(w, r, redirectTarget, 302) 90 | } 91 | 92 | func logoutHandler(w http.ResponseWriter, r *http.Request) { 93 | if r.Method == "POST" { 94 | clearSession(w) 95 | http.Redirect(w, r, "/", 302) 96 | } 97 | } 98 | 99 | func checkLogin(r *http.Request) bool { 100 | if cookie, err := r.Cookie("session"); err == nil { 101 | cookieValue := make(map[string]string) 102 | if err = cookieHandler.Decode("session", cookie.Value, &cookieValue); err == nil { 103 | check := cookieValue["Login"] 104 | return check != "" 105 | } 106 | } 107 | return false 108 | } 109 | 110 | func setSession(w http.ResponseWriter) { 111 | value := map[string]string{ 112 | "Login": time.Now().String(), 113 | } 114 | if encoded, err := cookieHandler.Encode("session", value); err == nil { 115 | cookie := &http.Cookie{ 116 | Name: "session", 117 | Value: encoded, 118 | Path: "/", 119 | Secure: config.Config.HTTPSSL, 120 | HttpOnly: true, 121 | } 122 | http.SetCookie(w, cookie) 123 | } 124 | } 125 | 126 | func clearSession(w http.ResponseWriter) { 127 | cookie := &http.Cookie{ 128 | Name: "session", 129 | Value: "", 130 | Path: "/", 131 | MaxAge: -1, 132 | } 133 | http.SetCookie(w, cookie) 134 | } 135 | -------------------------------------------------------------------------------- /dbshield/httpserver/server_test.go: -------------------------------------------------------------------------------- 1 | package httpserver 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | "net/http/httptest" 10 | "net/url" 11 | "strings" 12 | "testing" 13 | "time" 14 | 15 | "github.com/boltdb/bolt" 16 | "github.com/nim4/DBShield/dbshield/config" 17 | "github.com/nim4/DBShield/dbshield/sql" 18 | "github.com/nim4/DBShield/dbshield/training" 19 | ) 20 | 21 | func TestMain(m *testing.M) { 22 | config.Config.HTTPAddr = ":-1" 23 | config.Config.HTTPPassword = "foo" 24 | 25 | tmpfile, err := ioutil.TempFile("", "testdb") 26 | if err != nil { 27 | panic(err) 28 | } 29 | defer tmpfile.Close() 30 | path := tmpfile.Name() 31 | training.DBCon, err = bolt.Open(path, 0600, nil) 32 | if err != nil { 33 | panic(err) 34 | } 35 | training.DBCon.Update(func(tx *bolt.Tx) error { 36 | tx.CreateBucket([]byte("pattern")) 37 | tx.CreateBucket([]byte("abnormal")) 38 | tx.CreateBucket([]byte("state")) 39 | return nil 40 | }) 41 | m.Run() 42 | } 43 | 44 | func TestServe(t *testing.T) { 45 | err := Serve() 46 | if err == nil { 47 | t.Error("Expected error") 48 | } 49 | config.Config.HTTPSSL = true 50 | err = Serve() 51 | if err == nil { 52 | t.Error("Expected error") 53 | } 54 | } 55 | 56 | func TestMainHandler(t *testing.T) { 57 | r, err := http.NewRequest("GET", "/", nil) 58 | if err != nil { 59 | t.Error("Got an error ", err) 60 | } 61 | 62 | w := httptest.NewRecorder() 63 | mainHandler(w, r) 64 | 65 | if w.Code != 200 { 66 | t.Error("Expected 200 got ", w.Code) 67 | } 68 | 69 | r, err = http.NewRequest("GET", "/", nil) 70 | if err != nil { 71 | t.Error("Got an error ", err) 72 | } 73 | w = httptest.NewRecorder() 74 | setSession(w) 75 | r.Header.Set("Cookie", w.HeaderMap.Get("Set-Cookie")) 76 | mainHandler(w, r) 77 | body, err := ioutil.ReadAll(w.Body) 78 | if err != nil { 79 | t.Error("Got an error ", err) 80 | } 81 | if strings.Index(string(body), "\"Logout\"") == -1 { 82 | t.Error("Expected report page") 83 | } 84 | } 85 | 86 | func TestAPIHandler(t *testing.T) { 87 | defer recover() 88 | r, err := http.NewRequest("GET", "/", nil) 89 | if err != nil { 90 | t.Error("Got an error ", err) 91 | } 92 | w := httptest.NewRecorder() 93 | apiHandler(w, r) 94 | body, err := ioutil.ReadAll(w.Body) 95 | if err != nil { 96 | t.Error("Got an error ", err) 97 | } 98 | if len(body) != 0 { 99 | t.Error("Expected 0 length got", len(body)) 100 | } 101 | setSession(w) 102 | r.Header.Set("Cookie", w.HeaderMap.Get("Set-Cookie")) 103 | apiHandler(w, r) 104 | body, _ = ioutil.ReadAll(w.Body) 105 | var j struct { 106 | Total int 107 | Abnormal int 108 | } 109 | err = json.Unmarshal(body, &j) 110 | if err != nil { 111 | t.Error("Got an error ", err) 112 | } 113 | if j.Total != 0 || j.Abnormal != 0 { 114 | t.Error("Expected 0, 0 got", j) 115 | } 116 | 117 | j.Total = 1 118 | j.Abnormal = 1 119 | 120 | c1 := sql.QueryContext{ 121 | Query: []byte("select * from test;"), 122 | Database: []byte("test"), 123 | User: []byte("test"), 124 | Client: []byte("127.0.0.1"), 125 | Time: time.Now(), 126 | } 127 | c2 := sql.QueryContext{ 128 | Query: []byte("select * from user;"), 129 | Database: []byte("test"), 130 | User: []byte("test"), 131 | Client: []byte("127.0.0.1"), 132 | Time: time.Now(), 133 | } 134 | err = training.AddToTrainingSet(c1) 135 | if err != nil { 136 | t.Error("Got an error ", err) 137 | } 138 | training.CheckQuery(c2) 139 | apiHandler(w, r) 140 | body, _ = ioutil.ReadAll(w.Body) 141 | err = json.Unmarshal(body, &j) 142 | fmt.Println(j) 143 | if err != nil { 144 | t.Error("Got an error ", err) 145 | } 146 | if j.Total != 2 || j.Abnormal != 1 { 147 | t.Error("Expected 1, 1 got", j) 148 | } 149 | 150 | tmpCon := training.DBCon 151 | defer func() { 152 | training.DBCon = tmpCon 153 | }() 154 | tmpfile, err := ioutil.TempFile("", "testdb") 155 | if err != nil { 156 | panic(err) 157 | } 158 | defer tmpfile.Close() 159 | path := tmpfile.Name() 160 | training.DBCon, err = bolt.Open(path, 0600, nil) 161 | if err != nil { 162 | t.Error("Got an error ", err) 163 | } 164 | apiHandler(w, r) 165 | body, err = ioutil.ReadAll(w.Body) 166 | if err != nil { 167 | t.Error("Got an error ", err) 168 | } 169 | err = json.Unmarshal(body, &j) 170 | if err != nil { 171 | t.Error("Got an error ", err) 172 | } 173 | 174 | apiHandler(w, r) 175 | body, err = ioutil.ReadAll(w.Body) 176 | if err != nil { 177 | t.Error("Got an error ", err) 178 | } 179 | err = json.Unmarshal(body, &j) 180 | if err != nil { 181 | t.Error("Got an error ", err) 182 | } 183 | } 184 | 185 | func TestLoginHandler(t *testing.T) { 186 | data := url.Values{} 187 | data.Add("password", "bar") 188 | r, err := http.NewRequest("POST", "/", bytes.NewBufferString(data.Encode())) 189 | if err != nil { 190 | t.Error("Got an error ", err) 191 | } 192 | 193 | r.Header.Add("Content-Type", "application/x-www-form-urlencoded") 194 | 195 | w := httptest.NewRecorder() 196 | loginHandler(w, r) 197 | 198 | if w.Code != 302 { 199 | t.Error("Expected 302 got ", w.Code) 200 | } 201 | 202 | if w.HeaderMap.Get("Location") != "/" { 203 | t.Error("Expected / got ", w.HeaderMap.Get("Location")) 204 | } 205 | 206 | data.Set("password", config.Config.HTTPPassword) 207 | r, err = http.NewRequest("POST", "/", bytes.NewBufferString(data.Encode())) 208 | if err != nil { 209 | t.Error("Got an error ", err) 210 | } 211 | 212 | r.Header.Add("Content-Type", "application/x-www-form-urlencoded") 213 | 214 | w = httptest.NewRecorder() 215 | loginHandler(w, r) 216 | 217 | if w.Code != 302 { 218 | t.Error("Expected 302 got ", w.Code) 219 | } 220 | 221 | if w.HeaderMap.Get("Location") != "/report.htm" { 222 | t.Error("Expected /report.htm got ", w.HeaderMap.Get("Location")) 223 | } 224 | } 225 | 226 | func TestLogoutHandler(t *testing.T) { 227 | r, err := http.NewRequest("POST", "/", nil) 228 | if err != nil { 229 | t.Error("Got an error ", err) 230 | } 231 | 232 | w := httptest.NewRecorder() 233 | logoutHandler(w, r) 234 | 235 | if w.Code != 302 { 236 | t.Error("Expected 302 got ", w.Code) 237 | } 238 | } 239 | 240 | func TestCheckLogin(t *testing.T) { 241 | r, err := http.NewRequest("GET", "/", nil) 242 | if err != nil { 243 | t.Error("Got an error ", err) 244 | } 245 | 246 | cookie := &http.Cookie{ 247 | Name: "session", 248 | Value: "XYZ", 249 | Path: "/", 250 | Secure: true, 251 | HttpOnly: true, 252 | } 253 | r.AddCookie(cookie) 254 | if checkLogin(r) { 255 | t.Error("Expected false got true") 256 | } 257 | 258 | r, err = http.NewRequest("GET", "/", nil) 259 | if err != nil { 260 | t.Error("Got an error ", err) 261 | } 262 | w := httptest.NewRecorder() 263 | setSession(w) 264 | r.Header.Set("Cookie", w.HeaderMap.Get("Set-Cookie")) 265 | 266 | if !checkLogin(r) { 267 | t.Error("Expected true got false") 268 | } 269 | } 270 | 271 | func TestSetSession(t *testing.T) { 272 | w := httptest.NewRecorder() 273 | setSession(w) 274 | if strings.Index(w.HeaderMap.Get("Set-Cookie"), "session=") == -1 { 275 | t.Error("Expected session cookie") 276 | } 277 | } 278 | 279 | func TestClearSession(t *testing.T) { 280 | w := httptest.NewRecorder() 281 | clearSession(w) 282 | if strings.Index(w.HeaderMap.Get("Set-Cookie"), "session=;") == -1 { 283 | t.Error("Expected empty session cookie") 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /dbshield/logger/log.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | ) 8 | 9 | const ( 10 | flagWarning = 0x1 11 | flagInfo = 0x2 12 | flagDebug = 0x4 13 | ) 14 | 15 | var ( 16 | warn bool 17 | info bool 18 | debug bool 19 | ) 20 | 21 | //Output of logging functions 22 | var Output *os.File 23 | 24 | //Init the log output and logging level 25 | func Init(path string, level uint) error { 26 | switch path { 27 | case "stdout": 28 | Output = os.Stdout 29 | case "stderr": 30 | Output = os.Stderr 31 | default: 32 | var err error 33 | Output, err = os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) 34 | if err != nil { 35 | return fmt.Errorf("Error opening log file: %v", err) 36 | } 37 | } 38 | log.SetOutput(Output) 39 | warn = level&flagWarning == flagWarning 40 | info = level&flagInfo == flagInfo 41 | debug = level&flagDebug == flagDebug 42 | return nil 43 | } 44 | 45 | func println(title string, msg ...interface{}) { 46 | arg := append([]interface{}{title}, msg...) 47 | log.Println(arg...) 48 | } 49 | 50 | func printFormat(format string, msg ...interface{}) { 51 | log.Printf(format, msg...) 52 | } 53 | 54 | //Debug level > 1 logging with "[DEBUG]" prefix 55 | func Debug(msg ...interface{}) { 56 | if debug { 57 | println("[DEBUG]", msg...) 58 | } 59 | } 60 | 61 | //Debugf level > 1 format logging with "[DEBUG]" prefix 62 | func Debugf(format string, msg ...interface{}) { 63 | if debug { 64 | printFormat("[DEBUG] "+format, msg...) 65 | } 66 | } 67 | 68 | //Info level > 0 logging with "[INFO]" prefix 69 | func Info(msg ...interface{}) { 70 | if info { 71 | println("[INFO] ", msg...) 72 | } 73 | } 74 | 75 | //Infof level > 0 format logging with "[INFO]" prefix 76 | func Infof(format string, msg ...interface{}) { 77 | if info { 78 | printFormat("[INFO] "+format, msg...) 79 | } 80 | } 81 | 82 | //Warning any level logging with "[WARN]" prefix 83 | func Warning(msg ...interface{}) { 84 | if warn { 85 | println("[WARN] ", msg...) 86 | } 87 | } 88 | 89 | //Warningf any level format logging with "[WARN]" prefix 90 | func Warningf(format string, msg ...interface{}) { 91 | if warn { 92 | printFormat("[WARN] "+format, msg...) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /dbshield/logger/log_test.go: -------------------------------------------------------------------------------- 1 | package logger_test 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/nim4/DBShield/dbshield/logger" 8 | ) 9 | 10 | func TestLogger(t *testing.T) { 11 | logger.Init("stderr", 7) 12 | format := "%s" 13 | msg := "Test" 14 | logger.Debug(msg) 15 | logger.Debugf(format, msg) 16 | logger.Info(msg) 17 | logger.Infof(format, msg) 18 | logger.Warning(msg) 19 | logger.Warningf(format, msg) 20 | } 21 | 22 | func TestInit(t *testing.T) { 23 | 24 | err := logger.Init(os.TempDir(), 0) 25 | if err == nil { 26 | t.Error("Expected error") 27 | } 28 | 29 | err = logger.Init("stdout", 0) 30 | if err != nil { 31 | t.Error("Got error", err) 32 | } 33 | 34 | err = logger.Init("stderr", 7) 35 | if err != nil { 36 | t.Error("Got error", err) 37 | } 38 | } 39 | 40 | func BenchmarkDebugf(b *testing.B) { 41 | logger.Init("stderr", 7) 42 | for i := 0; i < b.N; i++ { 43 | logger.Debugf("%s", "t") 44 | } 45 | } 46 | 47 | func BenchmarkDebug(b *testing.B) { 48 | logger.Init("stderr", 7) 49 | for i := 0; i < b.N; i++ { 50 | logger.Debug("t") 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /dbshield/sql/sql.go: -------------------------------------------------------------------------------- 1 | package sql 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "sync" 7 | "time" 8 | 9 | "github.com/xwb1989/sqlparser" 10 | ) 11 | 12 | //QueryContext holds information around query 13 | type QueryContext struct { 14 | Query []byte 15 | User []byte 16 | Client []byte 17 | Database []byte 18 | Time time.Time 19 | } 20 | 21 | //Unmarshal []byte into QueryContext 22 | func (c *QueryContext) Unmarshal(b []byte) (size uint32) { 23 | n := binary.BigEndian.Uint32(b) 24 | b = b[4:] 25 | 26 | c.Query = b[:n] 27 | size = n 28 | 29 | b = b[n:] 30 | n = binary.BigEndian.Uint32(b) 31 | b = b[4:] 32 | c.User = b[:n] 33 | size += n 34 | 35 | b = b[n:] 36 | n = binary.BigEndian.Uint32(b) 37 | b = b[4:] 38 | c.Client = b[:n] 39 | size += n 40 | 41 | b = b[n:] 42 | n = binary.BigEndian.Uint32(b) 43 | b = b[4:] 44 | c.Database = b[:n] 45 | size += n 46 | 47 | c.Time.UnmarshalBinary(b[n:]) 48 | size += 8 49 | return 50 | } 51 | 52 | var bufPool = sync.Pool{ 53 | New: func() interface{} { 54 | return new(bytes.Buffer) 55 | }, 56 | } 57 | 58 | //Marshal load []byte into QueryContext 59 | func (c *QueryContext) Marshal() []byte { 60 | buf := bufPool.Get().(*bytes.Buffer) 61 | defer bufPool.Put(buf) 62 | buf.Reset() 63 | l := make([]byte, 4) 64 | binary.BigEndian.PutUint32(l, uint32(len(c.Query))) 65 | buf.Write(l) 66 | buf.Write(c.Query) 67 | 68 | binary.BigEndian.PutUint32(l, uint32(len(c.User))) 69 | buf.Write(l) 70 | buf.Write(c.User) 71 | 72 | binary.BigEndian.PutUint32(l, uint32(len(c.Client))) 73 | buf.Write(l) 74 | buf.Write(c.Client) 75 | 76 | binary.BigEndian.PutUint32(l, uint32(len(c.Database))) 77 | buf.Write(l) 78 | buf.Write(c.Database) 79 | 80 | t, _ := c.Time.MarshalBinary() 81 | buf.Write(t) 82 | return buf.Bytes() 83 | } 84 | 85 | //Pattern returns pattern of given query 86 | func Pattern(query []byte) []byte { 87 | tokenizer := sqlparser.NewStringTokenizer(string(query)) 88 | buf := bytes.Buffer{} 89 | l := make([]byte, 4) 90 | for { 91 | typ, val := tokenizer.Scan() 92 | switch typ { 93 | case sqlparser.ID: //table, database, variable & ... names 94 | buf.Write(val) 95 | case 0: //End of query 96 | return buf.Bytes() 97 | default: 98 | binary.BigEndian.PutUint32(l, uint32(typ)) 99 | buf.Write(l) 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /dbshield/sql/sql_test.go: -------------------------------------------------------------------------------- 1 | package sql_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | "time" 7 | 8 | "github.com/nim4/DBShield/dbshield/sql" 9 | ) 10 | 11 | func TestQueryContext(t *testing.T) { 12 | c := sql.QueryContext{ 13 | Query: []byte("select * from test;"), 14 | Database: []byte("test"), 15 | User: []byte("test"), 16 | Client: []byte("127.0.0.1"), 17 | Time: time.Now(), 18 | } 19 | r := c 20 | b := c.Marshal() 21 | c.Unmarshal(b) 22 | 23 | if bytes.Compare(c.Query, r.Query) != 0 { 24 | t.Error("Expected Query:", r.Query, "got", c.Query) 25 | } 26 | 27 | if bytes.Compare(c.Query, r.Query) != 0 { 28 | t.Error("Expected Database:", r.Database, "got", c.Database) 29 | } 30 | 31 | if bytes.Compare(c.User, r.User) != 0 { 32 | t.Error("Expected User:", r.User, "got", c.User) 33 | } 34 | 35 | if bytes.Compare(c.Client, r.Client) != 0 { 36 | t.Error("Expected Client:", r.Client, "got", c.Client) 37 | } 38 | 39 | if c.Time.Unix() != r.Time.Unix() { 40 | t.Error("Expected Time:", r.Time, "got", c.Time) 41 | } 42 | } 43 | 44 | func TestPattern(t *testing.T) { 45 | p := sql.Pattern([]byte("select * from X;")) 46 | if len(p) < 4 { 47 | t.Error("Unexpected Pattern") 48 | } 49 | } 50 | 51 | func BenchmarkPattern(b *testing.B) { 52 | q := []byte("select * from test;") 53 | b.ReportAllocs() 54 | b.ResetTimer() 55 | for i := 0; i < b.N; i++ { 56 | sql.Pattern(q) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /dbshield/training/training.go: -------------------------------------------------------------------------------- 1 | package training 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "sync/atomic" 8 | 9 | "github.com/boltdb/bolt" 10 | "github.com/nim4/DBShield/dbshield/config" 11 | "github.com/nim4/DBShield/dbshield/logger" 12 | "github.com/nim4/DBShield/dbshield/sql" 13 | ) 14 | 15 | var ( 16 | //QueryCounter state 17 | QueryCounter = uint64(0) 18 | 19 | //AbnormalCounter state 20 | AbnormalCounter = uint64(0) 21 | 22 | //DBCon holds a pointer to our local database connection 23 | DBCon *bolt.DB 24 | 25 | errInvalidParrent = errors.New("Invalid pattern") 26 | errInvalidUser = errors.New("Invalid user") 27 | errInvalidClient = errors.New("Invalid client") 28 | ) 29 | 30 | //AddToTrainingSet records query context in local database 31 | func AddToTrainingSet(context sql.QueryContext) error { 32 | atomic.AddUint64(&QueryCounter, 1) 33 | pattern := sql.Pattern(context.Query) 34 | //logger.Debug("Pattern", pattern) 35 | 36 | if err := DBCon.Update(func(tx *bolt.Tx) error { 37 | b := tx.Bucket([]byte("pattern")) 38 | if b == nil { 39 | return errors.New("Invalid DB") 40 | } 41 | if b.Get(pattern) == nil { 42 | b.Put(pattern, context.Query) 43 | } 44 | 45 | uKey := bytes.Buffer{} 46 | uKey.Write(pattern) 47 | uKey.WriteString("_user_") 48 | uKey.Write(context.User) 49 | b.Put(uKey.Bytes(), []byte{0x11}) 50 | 51 | cKey := bytes.Buffer{} 52 | cKey.Write(pattern) 53 | cKey.WriteString("_client_") 54 | cKey.Write(context.Client) 55 | b.Put(cKey.Bytes(), []byte{0x11}) 56 | return nil 57 | }); err != nil { 58 | logger.Warning(err) 59 | return err 60 | } 61 | return nil 62 | } 63 | 64 | //CheckQuery pattern, returns true if it finds the pattern 65 | //We should keep it as fast as possible 66 | func CheckQuery(context sql.QueryContext) bool { 67 | atomic.AddUint64(&QueryCounter, 1) 68 | pattern := sql.Pattern(context.Query) 69 | if err := DBCon.View(func(tx *bolt.Tx) error { 70 | b := tx.Bucket([]byte("pattern")) 71 | if b == nil { 72 | panic("Invalid DB") 73 | } 74 | if b.Get(pattern) == nil { 75 | return errInvalidParrent 76 | } 77 | 78 | key := bytes.Buffer{} 79 | if config.Config.CheckUser { 80 | key.Write(pattern) 81 | key.WriteString("_user_") 82 | key.Write(context.User) 83 | if b.Get(key.Bytes()) == nil { 84 | return errInvalidUser 85 | } 86 | } 87 | if config.Config.CheckSource { 88 | key.Reset() 89 | key.Write(pattern) 90 | key.WriteString("_client_") 91 | key.Write(context.Client) 92 | if b.Get(key.Bytes()) == nil { 93 | return errInvalidClient 94 | } 95 | } 96 | return nil 97 | }); err != nil { 98 | logger.Warning(err) 99 | //Record abnormal 100 | recordAbnormal(pattern, context) 101 | return false 102 | } 103 | return true 104 | } 105 | 106 | func recordAbnormal(pattern []byte, context sql.QueryContext) error { 107 | atomic.AddUint64(&AbnormalCounter, 1) 108 | return DBCon.Update(func(tx *bolt.Tx) error { 109 | b := tx.Bucket([]byte("abnormal")) 110 | if b == nil { 111 | panic("Invalid DB") 112 | } 113 | id, _ := b.NextSequence() 114 | buf := make([]byte, 8) 115 | binary.BigEndian.PutUint64(buf, uint64(id)) 116 | return b.Put(buf, context.Marshal()) 117 | }) 118 | } 119 | -------------------------------------------------------------------------------- /dbshield/training/training_test.go: -------------------------------------------------------------------------------- 1 | package training_test 2 | 3 | import ( 4 | "io/ioutil" 5 | "log" 6 | "testing" 7 | "time" 8 | 9 | "github.com/boltdb/bolt" 10 | "github.com/nim4/DBShield/dbshield/config" 11 | "github.com/nim4/DBShield/dbshield/sql" 12 | "github.com/nim4/DBShield/dbshield/training" 13 | ) 14 | 15 | func TestMain(m *testing.M) { 16 | log.SetOutput(ioutil.Discard) // Avoid log outputs 17 | tmpfile, err := ioutil.TempFile("", "testdb") 18 | if err != nil { 19 | panic(err) 20 | } 21 | defer tmpfile.Close() 22 | path := tmpfile.Name() 23 | training.DBCon, err = bolt.Open(path, 0600, nil) 24 | if err != nil { 25 | panic(err) 26 | } 27 | training.DBCon.Update(func(tx *bolt.Tx) error { 28 | tx.CreateBucket([]byte("pattern")) 29 | tx.CreateBucket([]byte("abnormal")) 30 | tx.CreateBucket([]byte("state")) 31 | return nil 32 | }) 33 | m.Run() 34 | } 35 | 36 | func TestAddToTrainingSet(t *testing.T) { 37 | var err error 38 | c := sql.QueryContext{ 39 | Query: []byte("select * from test;"), 40 | Database: []byte("test"), 41 | User: []byte("test"), 42 | Client: []byte("127.0.0.1"), 43 | Time: time.Now(), 44 | } 45 | err = training.AddToTrainingSet(c) 46 | if err != nil { 47 | t.Error("Not Expected error", err) 48 | } 49 | 50 | tmpCon := training.DBCon 51 | defer func() { 52 | training.DBCon = tmpCon 53 | }() 54 | tmpfile, err := ioutil.TempFile("", "testdb") 55 | if err != nil { 56 | panic(err) 57 | } 58 | defer tmpfile.Close() 59 | path := tmpfile.Name() 60 | training.DBCon, err = bolt.Open(path, 0600, nil) 61 | if err != nil { 62 | panic(err) 63 | } 64 | err = training.AddToTrainingSet(c) 65 | if err == nil { 66 | t.Error("Expected error") 67 | } 68 | } 69 | 70 | func TestCheckQuery(t *testing.T) { 71 | config.Config.CheckUser = true 72 | config.Config.CheckSource = true 73 | c1 := sql.QueryContext{ 74 | Query: []byte("select * from test;"), 75 | Database: []byte("test"), 76 | User: []byte("test"), 77 | Client: []byte("127.0.0.1"), 78 | Time: time.Now(), 79 | } 80 | c2 := sql.QueryContext{ 81 | Query: []byte("select * from user;"), 82 | Database: []byte("test"), 83 | User: []byte("test"), 84 | Client: []byte("127.0.0.1"), 85 | Time: time.Now(), 86 | } 87 | training.AddToTrainingSet(c1) 88 | if !training.CheckQuery(c1) { 89 | t.Error("Expected false") 90 | } 91 | if training.CheckQuery(c2) { 92 | t.Error("Expected true") 93 | } 94 | 95 | tmpCon := training.DBCon 96 | defer func() { 97 | training.DBCon = tmpCon 98 | }() 99 | tmpfile, err := ioutil.TempFile("", "testdb") 100 | if err != nil { 101 | panic(err) 102 | } 103 | defer tmpfile.Close() 104 | path := tmpfile.Name() 105 | training.DBCon, err = bolt.Open(path, 0600, nil) 106 | if err != nil { 107 | panic(err) 108 | } 109 | training.DBCon.Update(func(tx *bolt.Tx) error { 110 | tx.CreateBucket([]byte("pattern")) 111 | return err 112 | }) 113 | defer func() { 114 | if r := recover(); r == nil { 115 | t.Error("Expected panic") 116 | } 117 | }() 118 | training.CheckQuery(c1) 119 | } 120 | 121 | func BenchmarkAddToTrainingSet(b *testing.B) { 122 | b.ReportAllocs() 123 | b.ResetTimer() 124 | c := sql.QueryContext{ 125 | Query: []byte("select * from test;"), 126 | Database: []byte("test"), 127 | User: []byte("test"), 128 | Client: []byte("127.0.0.1"), 129 | Time: time.Now(), 130 | } 131 | for i := 0; i < b.N; i++ { 132 | training.AddToTrainingSet(c) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /dbshield/utils.go: -------------------------------------------------------------------------------- 1 | package dbshield 2 | 3 | import ( 4 | "encoding/binary" 5 | "fmt" 6 | "io" 7 | "net" 8 | "os" 9 | "os/signal" 10 | "strings" 11 | "time" 12 | 13 | "github.com/boltdb/bolt" 14 | "github.com/nim4/DBShield/dbshield/config" 15 | "github.com/nim4/DBShield/dbshield/dbms" 16 | "github.com/nim4/DBShield/dbshield/logger" 17 | "github.com/nim4/DBShield/dbshield/training" 18 | "github.com/nim4/DBShield/dbshield/utils" 19 | ) 20 | 21 | const ( 22 | mysql = iota 23 | mssql 24 | postgres 25 | db2 26 | oracle 27 | ) 28 | 29 | //initial boltdb database 30 | func initModel(path string) { 31 | logger.Infof("Internal DB: %s", path) 32 | if training.DBCon == nil { 33 | training.DBCon, _ = bolt.Open(path, 0600, nil) 34 | training.DBCon.Update(func(tx *bolt.Tx) error { 35 | tx.CreateBucketIfNotExists([]byte("pattern")) 36 | tx.CreateBucketIfNotExists([]byte("abnormal")) 37 | b, _ := tx.CreateBucketIfNotExists([]byte("state")) 38 | v := b.Get([]byte("QueryCounter")) 39 | if v != nil { 40 | training.QueryCounter = binary.BigEndian.Uint64(v) 41 | } 42 | v = b.Get([]byte("AbnormalCounter")) 43 | if v != nil { 44 | training.AbnormalCounter = binary.BigEndian.Uint64(v) 45 | } 46 | return nil 47 | }) 48 | } 49 | 50 | if config.Config.SyncInterval != 0 { 51 | training.DBCon.NoSync = true 52 | ticker := time.NewTicker(config.Config.SyncInterval) 53 | go func() { 54 | for range ticker.C { 55 | training.DBCon.Sync() 56 | } 57 | }() 58 | } 59 | } 60 | 61 | func closeHandlers() { 62 | if training.DBCon != nil { 63 | training.DBCon.Update(func(tx *bolt.Tx) error { 64 | //Supplied value must remain valid for the life of the transaction 65 | qCount := make([]byte, 8) 66 | abCount := make([]byte, 8) 67 | 68 | b := tx.Bucket([]byte("state")) 69 | binary.BigEndian.PutUint64(qCount, training.QueryCounter) 70 | b.Put([]byte("QueryCounter"), qCount) 71 | 72 | binary.BigEndian.PutUint64(abCount, training.AbnormalCounter) 73 | b.Put([]byte("AbnormalCounter"), abCount) 74 | 75 | return nil 76 | }) 77 | training.DBCon.Sync() 78 | training.DBCon.Close() 79 | } 80 | if logger.Output != nil { 81 | logger.Output.Close() 82 | } 83 | } 84 | 85 | //catching Interrupts 86 | func signalHandler() { 87 | term := make(chan os.Signal) 88 | signal.Notify(term, os.Interrupt) 89 | <-term 90 | logger.Info("Shutting down...") 91 | //Closing open handler politely 92 | closeHandlers() 93 | } 94 | 95 | //initLogging redirect log output to file/stdout/stderr 96 | func initLogging() { 97 | err := logger.Init(config.Config.LogPath, config.Config.LogLevel) 98 | if err != nil { 99 | panic(err) 100 | } 101 | } 102 | 103 | //maps database name to corresponding struct 104 | func dbNameToStruct(db string) (d uint, err error) { 105 | switch strings.ToLower(db) { 106 | case "db2": 107 | d = db2 108 | case "mssql": 109 | d = mssql 110 | case "mysql", "mariadb": 111 | d = mysql 112 | case "oracle": 113 | d = oracle 114 | case "postgres": 115 | d = postgres 116 | default: 117 | err = fmt.Errorf("Unknown DBMS: %s", db) 118 | } 119 | return 120 | } 121 | 122 | //generateDBMS instantiate a new instance of DBMS 123 | func generateDBMS() (utils.DBMS, func(io.Reader) ([]byte, error)) { 124 | switch config.Config.DB { 125 | case mssql: 126 | return new(dbms.MSSQL), dbms.MSSQLReadPacket 127 | case mysql: 128 | return new(dbms.MySQL), dbms.MySQLReadPacket 129 | case postgres: 130 | return new(dbms.Postgres), dbms.ReadPacket //TODO: implement explicit reader 131 | case oracle: 132 | return new(dbms.Oracle), dbms.ReadPacket //TODO: implement explicit reader 133 | case db2: 134 | return new(dbms.DB2), dbms.ReadPacket //TODO: implement explicit reader 135 | default: 136 | return nil, nil 137 | } 138 | } 139 | 140 | func handleClient(listenConn net.Conn, serverAddr *net.TCPAddr) error { 141 | d, reader := generateDBMS() 142 | logger.Debugf("Connected from: %s", listenConn.RemoteAddr()) 143 | serverConn, err := net.DialTCP("tcp", nil, serverAddr) 144 | if err != nil { 145 | logger.Warning(err) 146 | listenConn.Close() 147 | return err 148 | } 149 | if config.Config.Timeout > 0 { 150 | if err = listenConn.SetDeadline(time.Now().Add(config.Config.Timeout)); err != nil { 151 | return err 152 | } 153 | if err = serverConn.SetDeadline(time.Now().Add(config.Config.Timeout)); err != nil { 154 | return err 155 | } 156 | } 157 | logger.Debugf("Connected to: %s", serverConn.RemoteAddr()) 158 | d.SetSockets(listenConn, serverConn) 159 | d.SetCertificate(config.Config.TLSCertificate, config.Config.TLSPrivateKey) 160 | d.SetReader(reader) 161 | err = d.Handler() 162 | if err != nil { 163 | logger.Warning(err) 164 | } 165 | return err 166 | } 167 | -------------------------------------------------------------------------------- /dbshield/utils/action.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | //ActionDrop will close the connection 4 | func ActionDrop() error { 5 | panic("Dropping connection") 6 | } 7 | -------------------------------------------------------------------------------- /dbshield/utils/action_test.go: -------------------------------------------------------------------------------- 1 | package utils_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/nim4/DBShield/dbshield/utils" 7 | ) 8 | 9 | func TestActionDrop(t *testing.T) { 10 | defer func() { 11 | if r := recover(); r == nil { 12 | t.Errorf("Expected panic") 13 | } 14 | }() 15 | ret := utils.ActionDrop() 16 | if ret != nil { 17 | t.Error("Expected nil got ", ret) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /dbshield/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io" 5 | "net" 6 | ) 7 | 8 | //DBMS interface should get implemented with every added DBMS(MySQL, Postgre & etc.) structure 9 | type DBMS interface { 10 | DefaultPort() uint 11 | Close() 12 | SetReader(func(io.Reader) ([]byte, error)) 13 | Handler() error 14 | SetSockets(net.Conn, net.Conn) 15 | SetCertificate(string, string) error 16 | } 17 | -------------------------------------------------------------------------------- /dbshield/utils_test.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package dbshield 4 | 5 | import ( 6 | "net" 7 | "os" 8 | "testing" 9 | 10 | "github.com/nim4/DBShield/dbshield/config" 11 | "github.com/nim4/DBShield/dbshield/logger" 12 | "github.com/nim4/mock" 13 | ) 14 | 15 | func TestDbNameToStruct(t *testing.T) { 16 | _, err := dbNameToStruct("db2") 17 | if err != nil { 18 | t.Error("Expected struct, got ", err) 19 | return 20 | } 21 | _, err = dbNameToStruct("mysql") 22 | if err != nil { 23 | t.Error("Expected struct, got ", err) 24 | return 25 | } 26 | _, err = dbNameToStruct("oracle") 27 | if err != nil { 28 | t.Error("Expected struct, got ", err) 29 | return 30 | } 31 | _, err = dbNameToStruct("postgres") 32 | if err != nil { 33 | t.Error("Expected struct, got ", err) 34 | return 35 | } 36 | //Invalid case is tested in postConfig test 37 | } 38 | 39 | func TestInitLogging(t *testing.T) { 40 | config.Config.LogPath = "stdout" 41 | initLogging() 42 | } 43 | 44 | func TestHandleClient(t *testing.T) { 45 | var s mock.ConnMock 46 | err := handleClient(&s, nil) 47 | if err == nil { 48 | t.Error("Expected error got nil") 49 | } 50 | ls, _ := net.Listen("tcp4", "localhost:0") 51 | go func() { 52 | for { 53 | conn, _ := ls.Accept() 54 | conn.Close() 55 | } 56 | }() 57 | 58 | ra, err := net.ResolveTCPAddr("tcp4", ls.Addr().String()) 59 | if err != nil { 60 | t.Fatal(err) 61 | } 62 | err = handleClient(&s, ra) 63 | if err == nil { 64 | t.Error("Expected error got nil") 65 | } 66 | } 67 | 68 | func TestCloseHandlers(t *testing.T) { 69 | logger.Output = os.Stderr 70 | defer func() { 71 | if r := recover(); r != nil { 72 | t.Error("Panic!") 73 | } 74 | }() 75 | closeHandlers() 76 | } 77 | 78 | func TestGenerateDBMS(t *testing.T) { 79 | config.Config.DB = 0 80 | v, _ := generateDBMS() 81 | if v == nil { 82 | t.Error("Got nil") 83 | } 84 | 85 | config.Config.DB++ 86 | v, _ = generateDBMS() 87 | if v == nil { 88 | t.Error("Got nil") 89 | } 90 | 91 | config.Config.DB++ 92 | v, _ = generateDBMS() 93 | if v == nil { 94 | t.Error("Got nil") 95 | } 96 | 97 | config.Config.DB++ 98 | v, _ = generateDBMS() 99 | if v == nil { 100 | t.Error("Got nil") 101 | } 102 | 103 | config.Config.DB = 100 104 | v, _ = generateDBMS() 105 | if v != nil { 106 | t.Error("Expected nil") 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | _ "net/http/pprof" 7 | "runtime" 8 | 9 | "github.com/nim4/DBShield/dbshield" 10 | ) 11 | 12 | func usage(showUsage bool) { 13 | print("DBShield " + dbshield.Version + "\n") 14 | if showUsage { 15 | flag.Usage() 16 | } 17 | } 18 | 19 | func main() { 20 | runtime.GOMAXPROCS(runtime.NumCPU()) // For Go < 1.5 21 | 22 | config := flag.String("c", "/etc/dbshield.yml", "config `file`") 23 | listPatterns := flag.Bool("l", false, "get list of captured patterns") 24 | removePattern := flag.String("r", "", "remove a captured pattern") 25 | listAbnormals := flag.Bool("a", false, "get list of abnormal queries") 26 | showConfig := flag.Bool("k", false, "show parsed config and exit") 27 | showVersion := flag.Bool("version", false, "show version") 28 | showHelp := flag.Bool("h", false, "show help") 29 | purge := flag.Bool("purge", false, "remove internal database") 30 | //Parsing command line arguments 31 | flag.Parse() 32 | 33 | if *showHelp { 34 | usage(true) 35 | return 36 | } 37 | 38 | if *showVersion { 39 | usage(false) 40 | return 41 | } 42 | 43 | if err := dbshield.SetConfigFile(*config); err != nil { 44 | log.Println(err) 45 | return 46 | } 47 | 48 | if *listPatterns { 49 | dbshield.Patterns() 50 | return 51 | } 52 | 53 | if *removePattern != "" { 54 | dbshield.RemovePattern(*removePattern) 55 | return 56 | } 57 | 58 | if *listAbnormals { 59 | dbshield.Abnormals() 60 | return 61 | } 62 | 63 | if *showConfig { 64 | dbshield.ShowConfig() 65 | return 66 | } 67 | 68 | if *purge { 69 | dbshield.Purge() 70 | return 71 | } 72 | 73 | log.Println(dbshield.Start()) 74 | } 75 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "io/ioutil" 7 | "os" 8 | "testing" 9 | ) 10 | 11 | func TestEveryThing(t *testing.T) { 12 | usage(true) 13 | 14 | os.Args = []string{os.Args[0], "-k", "-c", "conf/dbshield.yml"} 15 | main() 16 | 17 | flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) 18 | os.Args = []string{os.Args[0], "-purge", "-c", "conf/dbshield.yml"} 19 | main() 20 | 21 | flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) 22 | os.Args = []string{os.Args[0], "-l", "-c", "conf/invalid.yml"} 23 | main() 24 | 25 | flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) 26 | os.Args = []string{os.Args[0], "-l", "-c", "conf/dbshield.yml"} 27 | main() 28 | 29 | flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) 30 | os.Args = []string{os.Args[0], "-a", "-c", "conf/dbshield.yml"} 31 | main() 32 | 33 | flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) 34 | os.Args = []string{os.Args[0], "-r", "XYZ", "-c", "conf/dbshield.yml"} 35 | main() 36 | 37 | flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) 38 | os.Args = []string{os.Args[0], "-version"} 39 | main() 40 | 41 | flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) 42 | os.Args = []string{os.Args[0], "-h"} 43 | main() 44 | 45 | flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ContinueOnError) 46 | dat, err := ioutil.ReadFile("conf/dbshield.yml") 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | path := os.TempDir() + "/tempconfig.yml" 51 | dat = bytes.Replace(dat, []byte("logPath: "), []byte("logPath: "+os.TempDir()+" #"), 1) 52 | err = ioutil.WriteFile(path, dat, 0600) 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | os.Args = []string{os.Args[0], "-c", path} 57 | defer func() { 58 | if r := recover(); r == nil { 59 | t.Error("Expected panic") 60 | } 61 | }() 62 | main() 63 | } 64 | -------------------------------------------------------------------------------- /misc/Diagrams.xml: -------------------------------------------------------------------------------- 1 | 7Vxtj5s4EP41+XJSVxiDgY+3b22lVl1tVurdp4oEJ0ElkAPSTfrrzwQbsAd2s4mhaZOVdpc4Nph5Zh6PZwZG+Ga5eZ/6q8XnJKDRyDSCzQjfjkzTdQj7WzRsywbLxWXDPA2DsgnVDePwJ+WNBm9dhwHNpI55kkR5uJIbp0kc02letvGxfpomz3K3WRLJV135cwoaxlM/gq1fwyBf8Nuyjbr9Aw3nC35lhAz+zcSffp+nyTrm1xuZeLb7Kb9e+uJcvH+28IPkudGE70b4Jk2SvDxabm5oVIhWiK0cd9/xbTXvlMb5PgOscsAPP1rzW78PU/rsF51IxE5xPUnZ0bw44jPOt0JKP2iah0xon/wJjR6SLMzDJGZfTZI8T5ZshJ+tSnRm4YayK14v8mXEPiJ2KAb/HYXzYlCerFhrlqfJd3qTREnK2uIkpkXjwl8Vl1xu5oW2XU3DPA03VzMxU3w9S+J8zCfGZVCcn246xYIqYTMdpsmS5umWdeEDGNblkK3QS66/z7U2OHbZtGgogse7+Vz/5tWZawzYAYehHRICILn1c3/iZ/TUAAj4vL5lNGVn6wEHRxDCCzi4RgsOxvE4OACHr3TCGsblzZ68eTzTSX/AWJalAIOggZCrNhPRAI3QigY2QPoVsxrs9gM/WxQS3n1oSFmWJyNr15xgQlqE6v9cp/RqnZXSDKOoMcgwCDGKM7OuQcgk2PjOcwLDcSr1OEroRDEGE8rccqDEbR0SNy+sVOPgyUBgyx6MlRC+0FI3MsglCjLGgLSE4dINpD9ds1sPuDRpHPxdeKvs4zTysyycytJmd51u/ylo68rAnmj4d9dQMDBveKBpyCZbSHRHcOVFaQB8XEWMzKn20znNG+sdlGxDcnaLTou2lEZ+Hv6Qr9gmTH6FhyRkc6mBUxd6lbSyZJ1OKR/VdGjVE2H5REQ5T3nH4DwMBn/b6LYqOmRvn2/XtAB3Y6k/OyhnUOtZBcF+qge9lfd3T6PC+2eXuA9LjTOND09PD6wJXaE9mILZYS4rZEqz8Kc/2XUoNI2LifW2r0f2bUEe65xRyk/KB/icJyI6y7vpY+VPw3j+VHy4fWcpvCBWnqN4wXBk8SO4dFaa01RvUwctuH3RwjvGC2xllnjBdL0uXqCbMC/ZxMUO/ywNYp8P55LSQiXfuUkv5FfSi62an3UgvRDF7tVlQxO92LYyX/QyvRD1/pBeerGg+ze++3R3UzDMX+z3/vHLZ/bvwZ9TdlfG1w93j3fs/8fbknYOYBrdDGAbsoRc6Bgg0uIX6CAAC0ZZNBEAYn4Blu3f2cP+Jdt3NNs+OTHXgjgy8qZxoO075is+iibjJ7h9wtqM2QbqWBjuU5gzZTTZcOM6Cbbl0RX7OStPwVa0BXkw4oTMnjwFS/sGglv8zlHAktkTm3SYfZNdLIVdXPf4TccrjgIy2gEahi1MZW9vHuopVFvPSpH6YQt1wuI62tgC7iwYDfjLwhyjyuhFC6cHSBjSCOoHbxuRl9zUHNKgrGZPdjstneH5z43XsOnJaiJSdE1es1rsSt1AH8JrwjYbOoSArNmQcJUVkaPnRZjTMRNJ8c1z6q+UoG0zstsMxfLAkxqFFfGoXZBKpPOKXdNKUNi4FD9v3om+BNA0+ghSKZtRB7qiGEMgsIbcko0AEHCFORsgbENOYgiqGwIIuKHC5wsEQTIQVZR7CCRgQN06XySwq+T1HBgo6w0JDyBxez1ehDQKACC/OImxSpPNtk5jHIh6A0xk9gCmpbigbXmq/rIhBC76AMY3bWZU3a/DmR6y5XAmEpsfPbuUko5+2ZbEVWB0DtySAH0QFqd5S4JNVe9ejl5aSJ2X3i0MgV7P76OJL3gvaq4TWvdLWvmG6Ll6JWIdpoAgca6eSFf43Gknvn3D59iW+h+vgNDHOE4BTywAvLeHjT2419Gjo8RDypXMw3S0c8q6o7xInrCFXL0694cnHTp1rvLoBYBuX7wI1lPXOUznOqese2FWeM7i+yxdOufChfZjzKCN/aJXXTB26jVKooTsJdf+pbq/ofx7T6aqKsfZLAg02/x7HSWBLgxhHEcwqrhqr8ogslfldrKHVBphOTJLlRkPfckL4T43sxduB2TDbBWwkuW28aFbBcWFt9WkqSZGUl1/y9Hr+ntwFfzNCheqqKRYG0xo5L1VLngwVXzGgfsqfC7ClO5wAWMPpoYh3Z4NEpZhyrRBoI/XFxJIXPsSu9+tDMpKUVHuIFC0Bu9fZe3zAceVs76WII1BwIGLx2+29oJYgAlppre1FxmQ8i91WhU0lhyTtAxIO73VaVUZ/AY09vmyjFrqNCzLwOr6i5XUxWHy5mFYK2l5Vu2RTpM0YG3/reluomE8S1ox4eVb8SQr/vlxsLvxvIgmnT6EHUEfHZBiIru+dsuTt62QqqGAwyCF++kzfg7UkdcgZ8Cn0xGC3tXlQdC6xs6RrYR4LoCmx+fTUctLHC6lLMds+M3XTa1PPNuKoBUcT6WCQCpcESyxh6QHKmYhCpDiTStvj1ArJ0JK8k1XzkwpfhBM0ln376maqjevW7314ZJkG4Z5lOor14YrSX9JNiSI5g/OssmE1fL4IHoDakOV5MlG7rqHspjCLpV26WYxhZVcUzcrwb3e3sG+d8WbCb48sj9odCqhP2TJuR7iQLPvL/TX8l62c867YTmKQVoqHXuLL5lwnwUZ+GygwK78YjzXhH54b1BguBbeLOj0++gSTjoidqsAKopamjyHegon2Xs873zYe9wmrm3ZLd7k/b1h7LzJSHF6pzTeOSrXr3rFHci1vi8uTvJwxjoX5/jGOu6csba3xHl8WhoQVUtqCIGIvrX+sQNQ9rF+VWnpNdSvg8V3/wM= -------------------------------------------------------------------------------- /misc/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nim4/DBShield/c888d9e40e13e04dbecc14389a7d252513bdf85f/misc/demo.gif -------------------------------------------------------------------------------- /misc/graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nim4/DBShield/c888d9e40e13e04dbecc14389a7d252513bdf85f/misc/graph.png -------------------------------------------------------------------------------- /misc/how_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nim4/DBShield/c888d9e40e13e04dbecc14389a7d252513bdf85f/misc/how_01.png -------------------------------------------------------------------------------- /misc/how_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nim4/DBShield/c888d9e40e13e04dbecc14389a7d252513bdf85f/misc/how_02.png -------------------------------------------------------------------------------- /misc/how_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nim4/DBShield/c888d9e40e13e04dbecc14389a7d252513bdf85f/misc/how_03.png -------------------------------------------------------------------------------- /misc/no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nim4/DBShield/c888d9e40e13e04dbecc14389a7d252513bdf85f/misc/no.png -------------------------------------------------------------------------------- /misc/scripts/README.md: -------------------------------------------------------------------------------- 1 | # Development Scripts 2 | 3 | ## pcap2bytearray 4 | Converts pcap file to go byte array format to simpilify making test cases. 5 | -------------------------------------------------------------------------------- /misc/scripts/pcap2bytearray.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | // Converts pcap file to go byte array format to simpilify making test cases. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "os" 11 | "strconv" 12 | 13 | "github.com/google/gopacket" 14 | "github.com/google/gopacket/layers" 15 | "github.com/google/gopacket/pcap" 16 | ) 17 | 18 | const fromClient = "0" 19 | const fromServer = "1" 20 | 21 | func main() { 22 | if len(os.Args) == 2 { 23 | var handle *pcap.Handle 24 | var err error 25 | if handle, err = pcap.OpenOffline(os.Args[1]); err != nil { 26 | log.Fatal("PCAP OpenOffline error:", err) 27 | } 28 | run(handle) 29 | } else { 30 | println("go run pcam2bytearray.go file.pcap") 31 | } 32 | } 33 | 34 | func hex(num int) string { 35 | str := "0x" 36 | if num < 16 { 37 | str += "0" 38 | } 39 | return fmt.Sprintf("%s%X", str, int(num)) 40 | } 41 | 42 | func run(src gopacket.PacketDataSource) { 43 | dec := gopacket.DecodersByLayerName["Ethernet"] 44 | source := gopacket.NewPacketSource(src, dec) 45 | var c, s string 46 | for packet := range source.Packets() { 47 | var str *string 48 | tcpLayer := packet.Layer(layers.LayerTypeTCP) 49 | if tcpLayer == nil { 50 | continue 51 | } 52 | tcp := tcpLayer.(*layers.TCP) 53 | payload := tcpLayer.LayerPayload() 54 | if len(payload) == 0 { 55 | continue 56 | } 57 | 58 | if _, err := strconv.Atoi(tcp.SrcPort.String()); err != nil { 59 | str = &s 60 | } else { 61 | str = &c 62 | } 63 | 64 | *str += " {\n " 65 | for i, b := range payload { 66 | *str += hex(int(b)) + ", " 67 | if (i+1)%12 == 0 { 68 | *str += "\n " 69 | } 70 | } 71 | *str += "\n },\n" 72 | } 73 | 74 | fmt.Println("c.Buffer = [][]byte{") 75 | fmt.Print(c) 76 | fmt.Println("}") 77 | fmt.Println("s.Buffer = [][]byte{") 78 | fmt.Print(s) 79 | fmt.Println("}") 80 | } 81 | -------------------------------------------------------------------------------- /misc/yes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nim4/DBShield/c888d9e40e13e04dbecc14389a7d252513bdf85f/misc/yes.png -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | echo "" > coverage.txt 5 | 6 | for d in $(go list ./... | grep -v /misc/) ; do 7 | go test -race -coverprofile=profile.out -covermode=atomic $d 8 | if [ -f profile.out ]; then 9 | cat profile.out >> coverage.txt 10 | rm profile.out 11 | fi 12 | done 13 | --------------------------------------------------------------------------------