13 \institute{schlittermann - internet \& unix support, Dresden} |
13 \institute{schlittermann - internet \& unix support, Dresden} |
14 |
14 |
15 |
15 |
16 \begin{document} |
16 \begin{document} |
17 |
17 |
18 % sicheres logging |
|
19 % einmal angenommen - verantwortugn |
|
20 |
|
21 \begin{frame} |
18 \begin{frame} |
22 \titlepage |
19 \titlepage |
23 \end{frame} |
20 \end{frame} |
24 |
21 |
25 \begin{frame}{Inhalt} |
22 \begin{frame}{Inhalt} |
26 \tableofcontents |
23 \tableofcontents |
27 \end{frame} |
24 \end{frame} |
28 |
25 |
29 \section{Positionierung} |
26 \section{Positionierung} |
30 |
27 |
31 \begin{frame}[<+->][fragile]{Wie positioniert Exim sich gegenüber anderen MTA} |
28 \begin{frame}[<+->][fragile]{Exim}{Entwicklung} |
32 \begin{itemize} |
29 \begin{itemize} |
33 \item seit 1995 Phil Hazel, seit ca. 2007 ca. 5…8 Aktive |
30 \item \textbf{Ex}perimental \textbf{I}nternet \textbf{M}ailer |
34 \item Lego vs. Playmobil (P. Heinlein) |
31 \item seit 1995 Phil Hazel, seit ca. 2007 ca. 5…8 Aktive Entwickler |
35 \item Klassisch Unix: Traditionelle Konfiguration und traditionelles Prozess-Management |
32 \item Klassisch Unix: Traditionelle Konfiguration und traditionelles Prozess-Management |
36 \item Flexibilität |
|
37 \item Sicherheit (sicher und zuverlässig) |
|
38 \item mehr als 90\% der Nachrichten werden sofort weitergeleitet bzw. ausgeliefert |
33 \item mehr als 90\% der Nachrichten werden sofort weitergeleitet bzw. ausgeliefert |
39 \begin{verbatim} |
34 \begin{verbatim} |
40 Time spent on the queue: messages with at least one remote delivery |
35 Time spent on the queue: messages with at least one remote delivery |
41 ------------------------------------------------------------------- |
36 ------------------------------------------------------------------- |
42 Under 1m 15052 99.2% 99.2% |
37 Under 1m 15052 99.2% 99.2% |
45 30m 91 0.6% 99.9% |
40 30m 91 0.6% 99.9% |
46 1h 9 0.1% 100.0% |
41 1h 9 0.1% 100.0% |
47 6h 2 0.0% 100.0% |
42 6h 2 0.0% 100.0% |
48 Over 1d 1 0.0% 100.0% |
43 Over 1d 1 0.0% 100.0% |
49 \end{verbatim} |
44 \end{verbatim} |
50 \item Saubere Dokumentation (spec.txt) |
45 \end{itemize} |
51 \item Dokumentiertes Protokollformat |
46 \end{frame} |
52 \end{itemize} |
47 |
|
48 \begin{frame}[<+->][fragile]{Exim}{Position} |
|
49 Ja, das ist Religionskrieg :-) |
|
50 \pause |
|
51 \begin{itemize} |
|
52 \item Lego vs. Playmobil (P. Heinlein) |
|
53 \item Anpassbarkeit |
|
54 \begin{itemize} |
|
55 \item keine Klassifizierung von Adressen |
|
56 \item keine \verb=mydestinations= |
|
57 \item Router sind Funktionsblöcke |
|
58 \item Intensiver Gebrauch von Expansionsmechanismen zur Laufzeit |
|
59 \end{itemize} |
|
60 \item Stabilität |
|
61 \item Sicherheit |
|
62 \item Definierte Dokumentation (Referenz-Handbuch mit Beispielen) |
|
63 \item Hervorragendes Debugging |
|
64 \end{itemize} |
|
65 \pause |
|
66 Nachteile? Klar - das Abarbeiten der Queue ist nicht sonderlich |
|
67 effizient gelöst. |
53 \end{frame} |
68 \end{frame} |
54 |
69 |
55 \section{Anatomie} |
70 \section{Anatomie} |
56 |
71 |
57 \begin{frame}{Arbeitsweise und Anatomie}{Überblick} |
72 \begin{frame}[<+->]{Arbeitsweise und Anatomie}{Überblick} |
58 \begin{itemize} |
73 \begin{itemize} |
59 \item Binary ist ein ca 1MB großer Universalklumpen |
74 \item Binary ist ein ca 1\,MB großer Universalklumpen |
60 \item Einfache Struktur der operativen Daten - Verzeichnis, 2 Files je |
75 \item Einfache Struktur der operativen Daten - Verzeichnis, 2 Files je |
61 Message, Spool/Message-Log in 16 Verzeichnissen |
76 Message, Spool/Message-Log in 16 Verzeichnissen |
62 \item Dokument |
77 \item Dokument |
63 \item Keine aufwändigen IPC - nichts, außer fork(2) oder exec(3) |
78 \item Keine aufwändigen IPC - nichts, außer fork(2) oder exec(3) |
64 \item Wenig gemeinsam genutzte Daten - nur „Hint“-Files (z.B. retry info) |
79 \item Wenig gemeinsam genutzte Daten - nur „Hint“-Files (z.B. retry info) |
|
80 \item Ohoh - setuid 0! |
65 \end{itemize} |
81 \end{itemize} |
66 \end{frame} |
82 \end{frame} |
67 |
83 |
68 \begin{frame}{Arbeitsweise und Anatomie}{Prozesse} |
84 \begin{frame}{Arbeitsweise und Anatomie}{Prozesse} |
69 \includegraphics[width=0.8\textwidth,angle=270]{procs} |
85 \includegraphics[width=0.8\textwidth,angle=270]{procs} |
70 \end{frame} |
86 \end{frame} |
71 |
87 |
72 \begin{frame}{Arbeitsweise und Anatomie}{IN, OUT, Retry} |
88 \begin{frame}[<+->]{Arbeitsweise und Anatomie}{IN, OUT, Retry} |
73 Es gibt im wesentlichen 3 Phasen der Verarbeitung |
89 Es gibt im wesentlichen 3 Phasen der Verarbeitung. |
|
90 \pause |
74 \begin{enumerate} |
91 \begin{enumerate} |
75 \item Empfang |
92 \item Empfang |
76 \begin{itemize} |
93 \begin{itemize} |
77 \item ACL mit Ratelimit, Blacklists, Routing-Test, Content-Scan |
94 \item ACL mit Ratelimit, Blacklists, Routing-Test, Content-Scan |
78 \item Ablage im Spool-Verzeichnis |
95 \item Ablage im Spool-Verzeichnis |
117 \item[rewrite] Umschreiben von Headern und Envelope |
135 \item[rewrite] Umschreiben von Headern und Envelope |
118 \item[authenticators] SMTP-Authentifizierung |
136 \item[authenticators] SMTP-Authentifizierung |
119 \end{description} |
137 \end{description} |
120 \end{frame} |
138 \end{frame} |
121 |
139 |
122 |
|
123 \subsection{Syntax} |
140 \subsection{Syntax} |
124 |
141 |
125 \begin{frame}[fragile]{Konfiguration}{Syntax} |
142 \begin{frame}[fragile]{Konfiguration}{Syntax} |
126 \begin{exampleblock}{Macros} |
143 \begin{exampleblock}{Macros} |
127 \begin{verbatim} |
144 \begin{verbatim} |
128 CF = /etc/exim4/ |
145 CF = /etc/exim4/ |
129 USER_BASE = ou=users,BASE |
146 USER_BASE = ou=users,BASE |
130 BASE = dc=example,dc=com |
147 BASE = dc=example,dc=com |
131 \end{verbatim} |
148 \end{verbatim} |
132 \end{exampleblock} |
149 \end{exampleblock} |
|
150 \pause |
133 |
151 |
134 \begin{exampleblock}{Bedingte Konfiguration} |
152 \begin{exampleblock}{Bedingte Konfiguration} |
135 \begin{verbatim} |
153 \begin{verbatim} |
136 .ifdef SMALL_MEM |
154 .ifdef SMALL_MEM |
137 message_size_limit = 50M |
155 message_size_limit = 50M |
138 .else |
156 .else |
139 message_size_limit = 500M |
157 message_size_limit = 500M |
140 .endif |
158 .endif |
141 \end{verbatim} |
159 \end{verbatim} |
142 \end{exampleblock} |
160 \end{exampleblock} |
|
161 \pause |
143 |
162 |
144 \begin{exampleblock}{Versteckte Optionen} |
163 \begin{exampleblock}{Versteckte Optionen} |
145 Wegen \verb=exim -bP …=: |
164 Wegen \verb=exim -bP …=: |
146 \begin{verbatim} |
165 \begin{verbatim} |
147 hide mysql_servers = localhost/mail/exim/secret |
166 hide mysql_servers = localhost/mail/exim/secret |
195 |
214 |
196 \begin{frame}[fragile]{Konfiguration}{Expansion} |
215 \begin{frame}[fragile]{Konfiguration}{Expansion} |
197 Etwa die Hälfte der Konfigurationsdirektiven erlaubt |
216 Etwa die Hälfte der Konfigurationsdirektiven erlaubt |
198 Variablensubstitution (Expansion) zur Laufzeit. |
217 Variablensubstitution (Expansion) zur Laufzeit. |
199 Testen kann man diese sehr einfach: |
218 Testen kann man diese sehr einfach: |
200 \begin{verbatim} |
219 \begin{alltt} |
201 $ exim -be '$primary_hostname' |
220 $ exim -be '$primary\_hostname' |
202 jumper.schlittermann.de |
221 jumper.schlittermann.de |
203 $ exim -be -oMi 1.1.1.1 -oMa 2.2.2.2 '$sender_host_address $received_ip_address' |
222 \pause |
|
223 $ exim -be -oMi 1.1.1.1 -oMa 2.2.2.2 '$sender\_host\_address $received\_ip\_address' |
204 2.2.2.2 1.1.1.1 |
224 2.2.2.2 1.1.1.1 |
205 $ exim -be '${lookup passwd{nobody}{${extract{5}{:}{$value}}}}' |
225 \pause |
|
226 $ exim -be '$\{lookup passwd\{nobody\}\{$\{extract\{5\}\{:\}\{$value\}\}\}\}' |
206 /nonexistent |
227 /nonexistent |
207 $ exim -be '${lookup{root}lsearch{/etc/aliases}}' |
228 \pause |
|
229 $ exim -be '$\{lookup\{root\}lsearch\{/etc/aliases\}\}' |
208 heiko |
230 heiko |
209 $ exim -be '${lookup dnsdb{mx=heise.de}}' |
231 \pause |
|
232 $ exim -be '$\{lookup dnsdb\{mx=heise.de\}\}' |
210 10 relay.heise.de |
233 10 relay.heise.de |
211 \end{verbatim} |
234 \end{alltt} |
212 \end{frame} |
235 \end{frame} |
213 |
236 |
214 \begin{frame}[fragile]{Expansion}{Übersicht} |
237 \begin{frame}[fragile]{Expansion}{Übersicht} |
215 Expansion als „working horse“ der Flexibilität |
238 Expansion als „working horse“ der Flexibilität |
216 \begin{description} |
239 \begin{description} |
322 \end{verbatim} |
345 \end{verbatim} |
323 \item[PSQL] \verb=${lookup pgsql{SELECT mailbox FROM …}}= |
346 \item[PSQL] \verb=${lookup pgsql{SELECT mailbox FROM …}}= |
324 \end{description} |
347 \end{description} |
325 \begin{exampleblock}{Lookup-Typen} |
348 \begin{exampleblock}{Lookup-Typen} |
326 dnsdb, ibase, ldap, mysql, nisplus, oracle, passwd, pgsql, sqlite |
349 dnsdb, ibase, ldap, mysql, nisplus, oracle, passwd, pgsql, sqlite |
|
350 \end{exampleblock} |
|
351 \end{frame} |
|
352 |
|
353 \begin{frame}[fragile]{String-Expansion}{Wenn nichts mehr geht} |
|
354 Als letzter Hilfe gibt es die Möglichkeit, Sockets auszulesen, externe |
|
355 Kommandos aufzurufen oder Perl-Funktionen zu nutzen. |
|
356 \begin{exampleblock}{Sockets} |
|
357 \begin{verbatim} |
|
358 condition = ${readsocket{<socket>}{<request>}} |
|
359 \end{verbatim} |
|
360 \end{exampleblock} |
|
361 \begin{exampleblock}{Kommandos} |
|
362 \begin{verbatim} |
|
363 domains = ${run{<command>[<arg>]…}} |
|
364 \end{verbatim} |
|
365 \end{exampleblock} |
|
366 \begin{exampleblock}{Perl} |
|
367 \begin{verbatim} |
|
368 perl_startup = do '/etc/exim/foo.pl' |
|
369 … |
|
370 domains = ${perl{<sub>}[{<arg>}…]} |
|
371 \end{verbatim} |
327 \end{exampleblock} |
372 \end{exampleblock} |
328 \end{frame} |
373 \end{frame} |
329 |
374 |
330 |
375 |
331 %\begin{frame}{ACL (SMTP-Phasen, Address-Überprüfung, Rate-Limiting, Content-Scan)} |
376 %\begin{frame}{ACL (SMTP-Phasen, Address-Überprüfung, Rate-Limiting, Content-Scan)} |
363 \item[fail] Bounce wird generiert |
410 \item[fail] Bounce wird generiert |
364 \item[defer] falscher Augenblick |
411 \item[defer] falscher Augenblick |
365 \item[error] Panik |
412 \item[error] Panik |
366 \end{description} |
413 \end{description} |
367 \item werden auch von ACL genutzt (Adressüberpüfung) |
414 \item werden auch von ACL genutzt (Adressüberpüfung) |
|
415 \item Router: dnslookup, manualroute, queryprogram, redirect, accept |
368 \item einfache Tests sind möglich mit \verb=exim -bt= (Adresstest - |
416 \item einfache Tests sind möglich mit \verb=exim -bt= (Adresstest - |
369 Routing) bzw. \verb=exim -bv= (Adressüberprüfung - ACL) |
417 Routing) bzw. \verb=exim -bv= (Adressüberprüfung - ACL) |
370 \end{itemize} |
418 \end{itemize} |
371 \end{frame} |
419 \end{frame} |
|
420 |
|
421 \subsection{Konfiguration} |
|
422 |
|
423 \begin{frame}[fragile]{Routing}{Konfiguration} |
|
424 Für jeden Router-Block |
|
425 \begin{itemize} |
|
426 \item Treiber |
|
427 \item Vorbedingungen (\verb=check_local_user=, \verb=domains=, \verb=condition=, …) |
|
428 \item Generische Optionen (\verb=caseful_local_part=, \verb=local_part_prefix=, …) |
|
429 \end{itemize} |
|
430 %\scriptsize |
|
431 \verbatiminput{routers.conf} |
|
432 \end{frame} |
|
433 |
|
434 \begin{frame}{Routing}{Schema} |
|
435 \includegraphics[width=0.8\textwidth,angle=270]{routing} |
|
436 \end{frame} |
|
437 |
|
438 \subsection{Test} |
372 |
439 |
373 \begin{frame}[fragile]{Routing}{Test} |
440 \begin{frame}[fragile]{Routing}{Test} |
374 \begin{verbatim} |
441 \begin{verbatim} |
375 $ exim -bt hans@example.com |
442 $ exim -bt hans@example.com |
376 hans@example.com |
443 hans@example.com |
418 \begin{alltt} |
477 \begin{alltt} |
419 \input{routinglocal.tt} |
478 \input{routinglocal.tt} |
420 \end{alltt} |
479 \end{alltt} |
421 \end{frame} |
480 \end{frame} |
422 |
481 |
423 % |
482 \section{Transport} |
424 %\begin{frame}{Transports (allg. Optionen und die einzelnen Transports smtp, appendfile, …)} |
483 |
425 %\end{frame} |
484 \begin{frame}[fragile]{Transports} |
426 % |
485 Die Router referenzieren ggf. einen „transport“. |
427 %\begin{frame}{Logging (mainlog, rejectlog, paniclog)} |
486 \begin{itemize} |
428 %\end{frame} |
487 \item Reihenfolge egal |
429 % |
488 \item Generische Optionen: \verb=headers_remove=, |
430 %\begin{frame}{Test und Betrieb} |
489 \verb=envelope_to_add=, \verb=transport_filter=, \verb=user= |
431 %\end{frame} |
490 \item Treiber hat weitere Optionen |
432 % |
491 \item Transports: appendfile, autoreply, lmtp, pipe, smtp |
433 %\begin{frame}{Sicherheitsbetrachtungen (Funktionssicherheit (Verlust von Mails), Einbruchssicherheit, Schreibrechte, Root-Rechte)} |
492 \item Default TLS, wenn STARTTLS geboten wird |
434 %\end{frame} |
493 \end{itemize} |
435 % |
494 \begin{alltt} |
|
495 \input{transports.conf} |
|
496 \end{alltt} |
|
497 \end{frame} |
|
498 |
|
499 \section{Access Control Lists} |
|
500 \subsection{Konfiguration} |
|
501 |
|
502 \begin{frame}[<+->][fragile]{Acess Control Lists} |
|
503 Für jede Phase der SMTP-Kommunikation gibt es einen ACL-Einstiegspunkt. |
|
504 Abarbeitung der Regeln bis zur Entscheidung. |
|
505 \pause |
|
506 \begin{description} |
|
507 \item[accept] alles gut, keine weiteren Regeln |
|
508 \item[deny] permanenter Fehler und passender Text dazu |
|
509 \item[require] permanenter Fehler oder nächste Regel |
|
510 \item[warn] Protokollierung und nächste Regel |
|
511 \item[defer] temporärer Fehler |
|
512 \end{description} |
|
513 \pause |
|
514 Verweis auf Blöcke im ACL-Abschnitt der Konfiguration |
|
515 \begin{verbatim} |
|
516 acl_smtp_connect = acl_check_connect |
|
517 acl_smtp_rcpt = acl_check_rcpt |
|
518 acl_smtp_data = acl_check_data |
|
519 \end{verbatim} |
|
520 \end{frame} |
|
521 |
|
522 \begin{frame}[fragile]{Access Control Lists}{Konfiguration} |
|
523 \tiny |
|
524 \verbatiminput{acl.conf} |
|
525 \end{frame} |
|
526 |
|
527 \subsection{Features} |
|
528 |
|
529 \begin{frame}[<+->][fragile]{Access Control Lists}{Features} |
|
530 \begin{itemize} |
|
531 \item Zugriff auf alles, was an Information verfügbar ist |
|
532 \item Ratelimit mit beliebigen Keys |
|
533 \item Überprüfung von Adressen \verb+verify = recipient+ |
|
534 \item Callout zur Überprüfung \verb+verify = recipient/callout=use_sender,defer_ok+ |
|
535 \item DNS-Blacklists \verb+dnslists = sbl.spamhaus.org+ |
|
536 \item Verschlüsselte Verbindung \verb+encrypted = *+ |
|
537 \item Content-Scan \verb+malware = *+, \verb+spam = …+ |
|
538 \item Header-Syntax \verb+verify = header_syntax+ |
|
539 \item Reverse-DNS \verb+verify = reverse_host_lookup+ |
|
540 \item ACL-Variablen für spätere Verarbeitung \verb+set acl_m_foo = bar+ |
|
541 \item Sub-ACL \verb+acl = foo_acl+ |
|
542 \item Generische Bedingung \verb+condition =+ |
|
543 \end{itemize} |
|
544 \end{frame} |
|
545 |
|
546 \section{Logging} |
|
547 |
|
548 \begin{frame}[fragile]{Logging} |
|
549 Sicherheit heißt auch Logging. Auskunft über das Verarbeiten der |
|
550 Nachricht. Gesteuert wird u.a. über \verb=log_selector=, \verb=log_write=, |
|
551 \verb=debug_print=. |
|
552 \begin{description} |
|
553 \item[mainlog] alle relevanten Transaktionen, dokumentiertes, |
|
554 maschinenlesbares Format |
|
555 \item[rejectlog] Details zu abgewiesenen Nachrichten |
|
556 \item[paniclog] Konfigurationsfehler, schwere Probleme |
|
557 \item[messagelog] Transaktionen zu einer spezifischen Nachricht |
|
558 \item[syslog] Fallback, wenn nicht mal mehr paniclog geht |
|
559 \end{description} |
|
560 \scriptsize |
|
561 \begin{alltt} |
|
562 \input{msglog.tt} |
|
563 \end{alltt} |
|
564 \end{frame} |
|
565 |
|
566 \begin{frame}[fragile]{Logging}{mainlog} |
|
567 \scriptsize |
|
568 \verbatiminput{mainlog.tt} |
|
569 \end{frame} |
|
570 |
|
571 \begin{frame}[fragile]{Logging}{rejectlog} |
|
572 \scriptsize |
|
573 \verbatiminput{rejectlog.tt} |
|
574 \end{frame} |
|
575 |
|
576 \section{Test und Betrieb} |
|
577 \subsection{Konfiguration} |
|
578 |
|
579 \begin{frame}[fragile]{Test und Betrieb}{Konfiguration} |
|
580 Viele Möglichkeiten, die bestehende Konfiguration zu überprüfen: |
|
581 \begin{alltt} |
|
582 $ exim -bV -C test.conf |
|
583 Configuration file is test.conf |
|
584 $ exim -bP primary\_hostname |
|
585 mail.example.com |
|
586 $ exim -bP routers |
|
587 … (ca 200 Zeilen) |
|
588 \end{alltt} |
|
589 \end{frame} |
|
590 |
|
591 \begin{frame}[fragile]{Test und Betrieb}{Routing, Expansion} |
|
592 \begin{verbatim} |
|
593 $ exim -d-all+route -bt hans@example.com |
|
594 … |
|
595 $ exim -d-all+expand -be '$lookup{root}lsearch{/etc/aliases}}' |
|
596 search_open: lsearch "/etc/aliases" |
|
597 search_find: file="/etc/aliases" |
|
598 key="root" partial=-1 affix=NULL starflags=0 |
|
599 LRU list: |
|
600 :/etc/aliases |
|
601 End |
|
602 internal_search_find: file="/etc/aliases" |
|
603 type=lsearch key="root" |
|
604 file lookup required for root |
|
605 in /etc/aliases |
|
606 lookup yielded: heiko |
|
607 heiko |
|
608 \end{verbatim} |
|
609 \end{frame} |
|
610 |
|
611 \begin{frame}[fragile]{Test und Betrieb}{ACL} |
|
612 Fake-SMTP-Session mit \verb=exim -bh 1.1.1.1=, aber einfacher noch mit swaks. |
|
613 \scriptsize |
|
614 \begin{verbatim} |
|
615 $ swaks --pipe 'exim -bh 1.1.1.1' --from … --to … |
|
616 === Trying pipe to exim -bh 1.1.1.1… |
|
617 === Connected to exim -bh 1.1.1.1. |
|
618 >>> looking up host name for 1.1.1.1 |
|
619 … |
|
620 <- **** SMTP testing session as if from host 1.1.1.1 |
|
621 <- **** This is not for real! |
|
622 … |
|
623 >>> processing "deny" |
|
624 >>> deny: condition test succeeded in ACL "acl_check_rcpt" |
|
625 LOG: [1967] H=(jumper.schlittermann.de) [1.1.1.1] |
|
626 F=<hs@schlittermann.de> rejected RCPT <hans@example.com>: relay not |
|
627 permitted |
|
628 <** 550 relay not permitted |
|
629 -> QUIT |
|
630 <- 221 jumper.schlittermann.de closing connection |
|
631 \end{verbatim} |
|
632 \end{frame} |
|
633 |
|
634 \section{Ausblick} |
|
635 |
|
636 \begin{frame}[<+->]{Was fehlt} |
|
637 Noch einige Dinge vergessen? |
|
638 \begin{itemize} |
|
639 \item TLS |
|
640 \item Header-Rewriting |
|
641 \item Retry-Rules |
|
642 \item SMTP-Authentication |
|
643 \item Cut-Through-Routing, PRDR, DANE, Enhanced Status Codes |
|
644 \end{itemize} |
|
645 \end{frame} |
|
646 |
|
647 \begin{frame}{DANKE} |
|
648 DANKE |
|
649 \end{frame} |
|
650 |
436 %\begin{frame}{Möglichkeiten zur Leistungsverbesserung (Warteschlange, Parallelisierung, Blockierung, Ratelimit)} |
651 %\begin{frame}{Möglichkeiten zur Leistungsverbesserung (Warteschlange, Parallelisierung, Blockierung, Ratelimit)} |
437 %\end{frame} |
652 %\end{frame} |
438 % |
653 % |
439 %\begin{frame}{Ausblick/Offene Punkte: PRDR, DANE, Enhanced Status Codes} |
654 %\begin{frame}{Ausblick/Offene Punkte: PRDR, DANE, Enhanced Status Codes} |
440 %\end{frame} |
655 %\end{frame} |