checker: deep redesign

This commit is contained in:
nemunaire 2021-03-05 15:14:45 +01:00
parent 4a27c9367e
commit 853477e54a
4 changed files with 132 additions and 38 deletions

View File

@ -7,6 +7,7 @@ import (
"log"
"net"
"net/http"
"strings"
"time"
"github.com/miekg/dns"
@ -102,21 +103,30 @@ func check_dns(domain, ip string) (aaaa net.IP, err error) {
// PORT 80
func check_http(ip string) (err error) {
func check_http(ip, dn string) (err error) {
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
req, errr := http.NewRequest("GET", fmt.Sprintf("http://[%s]/", ip), nil)
if errr != nil {
return errr
}
if dn != "" {
req.Header.Add("Host", strings.TrimSuffix(dn, "."))
}
var resp *http.Response
resp, err = client.Get(fmt.Sprintf("http://[%s]/", ip))
resp, err = client.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
if dn != "" && resp.StatusCode >= 400 {
return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status)
}
@ -128,12 +138,25 @@ func check_http(ip string) (err error) {
func check_https(domain, ip string) (err error) {
var resp *http.Response
resp, err = http.Get(fmt.Sprintf("https://%s/", domain))
resp, err = http.Get(fmt.Sprintf("https://%s/", strings.TrimSuffix(domain, ".")))
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode >= 300 && resp.StatusCode < 400 {
loc := resp.Header.Get("Location")
if loc != "" && strings.HasSuffix(dns.Fqdn(loc), domain) {
if dns.Fqdn(loc) == domain {
return fmt.Errorf("Redirection loop %s redirect to %s", domain, loc)
} else if err = check_https(dns.Fqdn(loc), ip); err != nil {
return fmt.Errorf("Error after following redirection to %s: %w", loc, err)
} else {
return
}
}
}
if resp.StatusCode >= 300 {
return fmt.Errorf("Bad status, got: %d (%s)", resp.StatusCode, resp.Status)
}
@ -212,26 +235,25 @@ func studentsChecker() {
if verbose {
log.Printf("%s just unlocked DNS challenge\n", std.Login)
}
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+2, ""); err != nil {
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+2, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+3, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
// Check HTTP with DNS
if addr == nil {
log.Printf("%s and HTTP (with DNS ip=%s): skipped due to empty response\n", std.Login, addr.String())
} else if err := check_http(addr.String()); err == nil {
} else if err := check_http(addr.String(), std.MyDelegatedDomain()); err == nil {
if verbose {
log.Printf("%s just unlocked HTTP challenge\n", std.Login)
}
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+0, ""); err != nil {
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+0, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+4, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
} else {
std.RegisterChallengeError(100*(tunnel_version-1)+4, err)
if verbose {
log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), err)
}
} else if verbose {
log.Printf("%s and HTTP (with DNS ip=%s): %s\n", std.Login, addr.String(), err)
}
// Check HTTPs with DNS
@ -241,27 +263,54 @@ func studentsChecker() {
if verbose {
log.Printf("%s just unlocked HTTPS challenge\n", std.Login)
}
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+1, ""); err != nil {
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+1, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+5, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
} else {
std.RegisterChallengeError(100*(tunnel_version-1)+5, err)
if verbose {
log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err)
}
} else if verbose {
log.Printf("%s and HTTPS (with DNS ip=%s): %s\n", std.Login, addr.String(), err)
}
} else {
// Check HTTP without DNS
if err := check_http(stdIP); err == nil {
if errreg := std.RegisterChallengeError(100*(tunnel_version-1)+3, err); errreg != nil {
log.Printf("Unable to register challenge error for %s: %s\n", std.Login, errreg)
}
if verbose {
log.Printf("%s and DNS: %s\n", std.Login, err)
}
}
// Check HTTP without DNS
if err := check_http(stdIP, ""); err == nil {
if verbose {
log.Printf("%s just unlocked HTTP IP (without DNS) challenge\n", std.Login)
}
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+0, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
} else {
std.RegisterChallengeError(100*(tunnel_version-1)+0, err)
if verbose {
log.Printf("%s and HTTP IP (without DNS): %s\n", std.Login, err)
}
}
// Check DNS for association
if addr, err := check_dns(std.MyAssociatedDomain(), DEFAULT_RESOLVER); err == nil {
// Check HTTP on delegated domain
if err := check_http(addr.String(), std.MyAssociatedDomain()); err == nil {
if verbose {
log.Printf("%s just unlocked HTTP challenge\n", std.Login)
log.Printf("%s just unlocked HTTP (without DNS) challenge\n", std.Login)
}
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+0, ""); err != nil {
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+0, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+1, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
} else {
std.RegisterChallengeError(100*(tunnel_version-1)+1, err)
if verbose {
log.Printf("%s and HTTP (without DNS): %s\n", std.Login, err)
}
} else if verbose {
log.Printf("%s and HTTP (without DNS): %s\n", std.Login, err)
}
// Check HTTPs without DNS
@ -269,13 +318,14 @@ func studentsChecker() {
if verbose {
log.Printf("%s just unlocked HTTPS challenge\n", std.Login)
}
if _, err := std.UnlockNewChallenge(100*(tunnel_version-1)+1, ""); err != nil {
if _, err := std.UpdateUnlockedChallenge(100*(tunnel_version-1)+1, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
if _, err := std.UnlockChallenge(100*(tunnel_version-1)+2, ""); err != nil {
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
}
} else {
std.RegisterChallengeError(100*(tunnel_version-1)+2, err)
if verbose {
log.Printf("%s and HTTPS (without DNS): %s\n", std.Login, err)
}
} else if verbose {
log.Printf("%s and HTTPS (without DNS): %s\n", std.Login, err)
}
}

View File

@ -95,6 +95,19 @@ CREATE TABLE IF NOT EXISTS student_challenges(
CONSTRAINT token_found UNIQUE (id_student,challenge),
FOREIGN KEY(id_student) REFERENCES students(id_student)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
return err
}
if _, err := db.Exec(`
CREATE TABLE IF NOT EXISTS student_challenge_errors(
id_st INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
id_student INTEGER NOT NULL,
challenge INTEGER NOT NULL,
time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
error VARCHAR(255) NOT NULL,
CONSTRAINT token_found UNIQUE (id_student,challenge),
FOREIGN KEY(id_student) REFERENCES students(id_student)
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
`); err != nil {
return err
}

View File

@ -3,6 +3,7 @@ package adlin
import (
"crypto/hmac"
"crypto/sha512"
"fmt"
"time"
)
@ -150,6 +151,18 @@ func (s Student) GetStatesByChallenge() (ucs []UnlockedChallenge, err error) {
}
}
func (s Student) UnlockChallenge(challenge int, value string) (uc UnlockedChallenge, err error) {
if uc, err = s.UnlockNewChallenge(challenge, value); err != nil {
if uc, err = s.UpdateUnlockedChallenge(challenge, value); err != nil {
return
}
}
s.RegisterChallengeError(challenge, fmt.Errorf("OK"))
return
}
func (s Student) UnlockNewChallenge(challenge int, value string) (UnlockedChallenge, error) {
if res, err := DBExec("INSERT INTO student_challenges (id_student, challenge, time, value) VALUES (?, ?, ?, ?)", s.Id, challenge, time.Now(), value); err != nil {
return UnlockedChallenge{}, err
@ -168,6 +181,24 @@ func (s Student) UpdateUnlockedChallenge(challenge int, value string) (UnlockedC
}
}
type ErroredChallenge struct {
Id int64 `json:"id,omitempty"`
IdStudent int64 `json:"id_student"`
Challenge int `json:"challenge,omitempty"`
Time time.Time `json:"time"`
Error string `json:"error,omitempty"`
}
func (s Student) RegisterChallengeError(challenge int, err error) error {
if _, errr := DBExec("INSERT INTO student_challenge_errors (id_student, challenge, time, error) VALUES (?, ?, ?, ?)", s.Id, challenge, time.Now(), err.Error()); errr == nil {
return nil
} else if _, errr := DBExec("UPDATE student_challenge_errors SET time = ?, error = ? WHERE id_student = ? AND challenge = ?", time.Now(), err.Error(), s.Id, challenge); errr != nil {
return errr
} else {
return nil
}
}
func (s Student) RegisterAccess(ip, mac string) error {
if res, err := DBExec("INSERT INTO student_login (id_student, ip, mac, time) VALUES (?, ?, ?, ?)", s.Id, ip, mac, time.Now()); err != nil {
return err

View File

@ -77,7 +77,7 @@
<div ng-repeat="(tutoid,tuto) in tuto_progress">
<hr>
<h6>TP {{tutoid+1}}</h6>
<span class="badge mr-1" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-warning': mychallenges[ch] && mychallenges[ch].recent && (tutoid != 0 && mychallenges[ch].recent > 300), 'badge-info': mychallenges[ch] && mychallenges[ch].recent && mychallenges[ch].recent <= 300 && mychallenges[ch].recent >= 120, 'badge-success': mychallenges[ch] && mychallenges[ch].recent && (tutoid == 0 || mychallenges[ch].recent < 120), 'badge-danger': !mychallenges[ch]}" title="{{ t.title }} @ {{ mychallenges[ch].time | date: 'medium' }} {{ mychallenges[ch].recent }}" ng-bind="t.label"></span>
<span class="badge mr-1" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-warning': mychallenges[ch] && mychallenges[ch].recent && (tutoid != 0 && mychallenges[ch].recent > 300), 'badge-info': mychallenges[ch] && mychallenges[ch].recent && mychallenges[ch].recent <= 300 && mychallenges[ch].recent >= 120, 'badge-success': mychallenges[ch] && mychallenges[ch].recent && (tutoid == 0 || mychallenges[ch].recent < 120), 'badge-danger': !mychallenges[ch] || !mychallenges[ch].recent}" title="{{ t.title }} @ {{ mychallenges[ch].time | date: 'medium' }} {{ mychallenges[ch].recent }}" ng-bind="t.label"></span>
</div>
</p>
</div>
@ -116,7 +116,7 @@
<a class="text-dark" href="/dashboard/{{login}}">{{ login }}</a>
</h5>
<div class="d-flex flex-wrap justify-content-around" style="padding: 0">
<span class="badge badge-sm" style="margin-left: .07rem" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-warning': mychallenges[ch] && mychallenges[ch].recent && (tutoid != 0 && mychallenges[ch].recent > 300), 'badge-info': mychallenges[ch] && mychallenges[ch].recent && mychallenges[ch].recent <= 300 && mychallenges[ch].recent >= 120, 'badge-success': mychallenges[ch] && mychallenges[ch].recent && (tutoid == 0 || mychallenges[ch].recent < 120), 'badge-danger': !mychallenges[ch]}" title="{{ t.title }} @ {{ mychallenges[ch].time | date: 'medium' }} {{ mychallenges[ch].recent }}" ng-bind="t.icon"></span>
<span class="badge badge-sm" style="margin-left: .07rem" ng-repeat="(ch,t) in tuto_progress[tutoid]" ng-class="{'badge-warning': mychallenges[ch] && mychallenges[ch].recent && (tutoid != 0 && mychallenges[ch].recent > 300), 'badge-info': mychallenges[ch] && mychallenges[ch].recent && mychallenges[ch].recent <= 300 && mychallenges[ch].recent >= 120, 'badge-success': mychallenges[ch] && mychallenges[ch].recent && (tutoid == 0 || mychallenges[ch].recent < 120), 'badge-danger': !mychallenges[ch] || !mychallenges[ch].recent}" title="{{ t.title }} @ {{ mychallenges[ch].time | date: 'medium' }} {{ mychallenges[ch].recent }}" ng-bind="t.icon"></span>
</div>
</div>
</div>